mathmaker
0.6(alpha)
|
00001 # -*- coding: utf-8 -*- 00002 00003 # Mathmaker creates automatically maths exercises sheets with their answers 00004 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net> 00005 00006 # This file is part of Mathmaker. 00007 00008 # Mathmaker is free software; you can redistribute it and/or modify 00009 # it under the terms of the GNU General Public License as published by 00010 # the Free Software Foundation; either version 3 of the License, or 00011 # any later version. 00012 00013 # Mathmaker is distributed in the hope that it will be useful, 00014 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 # GNU General Public License for more details. 00017 00018 # You should have received a copy of the GNU General Public License 00019 # along with Mathmaker; if not, write to the Free Software 00020 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00021 00022 # ------------------------------------------------------------------------------ 00023 # -------------------------------------------------------------------------- 00024 # ------------------------------------------------------------------------------ 00025 ## 00026 # @package core.root_calculus 00027 # @brief Mostly abstract classes for mathematical calculus objects. 00028 00029 from base import * 00030 from lib.common import alphabet 00031 from lib import is_ 00032 from lib.maths_lib import * 00033 from decimal import * 00034 from lib.common.cst import * 00035 import locale 00036 00037 markup_choice = cfg.get_value_from_file('MARKUP', 'USE') 00038 00039 if markup_choice == 'latex': 00040 from lib.common.latex import MARKUP 00041 00042 try: 00043 locale.setlocale(locale.LC_ALL, LANGUAGE + '.' + ENCODING) 00044 except: 00045 locale.setlocale(locale.LC_ALL, '') 00046 00047 00048 00049 00050 # ------------------------------------------------------------------------------ 00051 # -------------------------------------------------------------------------- 00052 # ------------------------------------------------------------------------------ 00053 ## 00054 # @class Evaluable 00055 # @brief Abstract mother class of all (evaluable) mathematical objects 00056 # It is not possible to implement any Evaluable object 00057 class Evaluable(Printable): 00058 00059 00060 00061 00062 00063 # -------------------------------------------------------------------------- 00064 ## 00065 # @brief If the object is literal, returns the first letter 00066 # The first term of a Sum, the first factor of a Product etc. 00067 def get_first_letter(self): 00068 raise error.MethodShouldBeRedefined(self, 'get_first_letter') 00069 00070 00071 00072 00073 00074 # -------------------------------------------------------------------------- 00075 ## 00076 # @brief Returns the numeric value of the object 00077 def evaluate(self): 00078 raise error.MethodShouldBeRedefined(self, 'evaluate') 00079 00080 00081 00082 00083 00084 # -------------------------------------------------------------------------- 00085 ## 00086 # @brief To check if this contains a rounded number... 00087 # @return True or False 00088 def contains_a_rounded_number(self): 00089 raise error.MethodShouldBeRedefined(self, 'contains_a_rounded_number') 00090 00091 00092 00093 00094 00095 # -------------------------------------------------------------------------- 00096 ## 00097 # @brief True if the object contains exactly the given objct 00098 # It can be used to detect objects embedded in a Sum or a Product that 00099 # contain only one term (or factor) 00100 # @param objct The object to search for 00101 # @return True if the object contains exactly the given objct 00102 def contains_exactly(self, objct): 00103 raise error.MethodShouldBeRedefined(self, 'contains_exactly') 00104 00105 00106 00107 00108 00109 # -------------------------------------------------------------------------- 00110 ## 00111 # @brief Sort order : numerics < sorted literals 00112 # @return -1, 0 or +1 00113 def alphabetical_order_cmp(self, other_objct): 00114 00115 if self.is_numeric() and other_objct.is_numeric(): 00116 return 0 00117 00118 elif self.is_literal() and other_objct.is_numeric(): 00119 return 1 00120 00121 elif self.is_numeric() and other_objct.is_literal(): 00122 return -1 00123 00124 elif self.is_literal() and other_objct.is_literal(): 00125 self_value = self.get_first_letter() 00126 other_value = other_objct.get_first_letter() 00127 00128 # let's compare 00129 if self_value == other_value: 00130 return 0 00131 elif alphabet.order[self_value] > alphabet.order[other_value]: 00132 return 1 00133 else: 00134 return -1 00135 00136 00137 00138 00139 00140 # -------------------------------------------------------------------------- 00141 ## 00142 # @brief True if the object only contains numeric objects 00143 def is_numeric(self): 00144 raise error.MethodShouldBeRedefined(self, 'is_numeric') 00145 00146 00147 00148 00149 00150 # -------------------------------------------------------------------------- 00151 ## 00152 # @brief True if the object only contains literal objects 00153 def is_literal(self): 00154 raise error.MethodShouldBeRedefined(self, 'is_literal') 00155 00156 00157 00158 00159 00160 # -------------------------------------------------------------------------- 00161 ## 00162 # @brief True if the evaluated value of an object is null 00163 def is_null(self): 00164 raise error.MethodShouldBeRedefined(self, 'is_null') 00165 00166 00167 00168 00169 00170 # ------------------------------------------------------------------------------ 00171 # -------------------------------------------------------------------------- 00172 # ------------------------------------------------------------------------------ 00173 ## 00174 # @class Calculable 00175 # @brief Abstract mother class of all (calculable) mathematical objects 00176 # It is not possible to implement any Calculable object 00177 class Calculable(Evaluable): 00178 00179 00180 00181 00182 # -------------------------------------------------------------------------- 00183 ## 00184 # @brief Returns the list of elements to iter over 00185 def get_iteration_list(self): 00186 raise error.MethodShouldBeRedefined(self, 'get_iteration_list') 00187 00188 00189 00190 00191 # -------------------------------------------------------------------------- 00192 ## 00193 # @brief Returns the next Calculable object during a numeric calculation 00194 def calculate_next_step(self, **options): 00195 raise error.MethodShouldBeRedefined(self, 'calculate_next_step') 00196 00197 00198 00199 00200 00201 # -------------------------------------------------------------------------- 00202 ## 00203 # @brief Returns the next step of expansion/reduction of the Sum 00204 # So, either the Sum of its expanded/reduced terms, 00205 # or the Sum itself reduced, or None 00206 # @return Exponented 00207 def expand_and_reduce_next_step(self, **options): 00208 raise error.MethodShouldBeRedefined(self, 00209 'expand_and_reduce_next_step') 00210 00211 00212 00213 00214 00215 # -------------------------------------------------------------------------- 00216 ## 00217 # @brief Returns the number of elements of the Exponented 00218 def __len__(self): 00219 raise error.MethodShouldBeRedefined(self, "__len__()") 00220 00221 00222 00223 00224 00225 # -------------------------------------------------------------------------- 00226 ## 00227 # @brief This will iter over the content of the Calculable 00228 def __iter__(self): 00229 return iter(self.get_iteration_list()) 00230 00231 def __next__(self): 00232 return next(self.get_iteration_list()) 00233 00234 00235 00236 00237 00238 # -------------------------------------------------------------------------- 00239 ## 00240 # @brief True if the usual writing rules require a × between two factors 00241 # @param objct The other one 00242 # @param position The position (integer) of self in the Product 00243 # @return True if the writing rules require × between self & obj 00244 def multiply_symbol_is_required(self, objct, position): 00245 raise error.MethodShouldBeRedefined(self, 00246 'multiply_symbol_is_required') 00247 00248 00249 00250 00251 00252 # -------------------------------------------------------------------------- 00253 ## 00254 # @brief True if the argument requires brackets in a product 00255 # For instance, a Sum with several terms or a negative Item 00256 # @param position The position of the object in the Product 00257 # @return True if the object requires brackets in a Product 00258 def requires_brackets(self, position): 00259 raise error.MethodShouldBeRedefined(self, 'requires_brackets') 00260 00261 00262 00263 00264 00265 # -------------------------------------------------------------------------- 00266 ## 00267 # @brief True if the argument requires inner brackets 00268 # The reason for requiring them is having an exponent different 00269 # from 1 and several terms or factors (in the case of Products & Sums) 00270 # @return True if the object requires inner brackets 00271 def requires_inner_brackets(self): 00272 raise error.MethodShouldBeRedefined(self, 00273 'requires_innner_brackets') 00274 00275 00276 00277 00278 00279 00280 # -------------------------------------------------------------------------- 00281 ## 00282 # @brief Uses the given lexicon to substitute literal Values in self 00283 def substitute(self, subst_dict): 00284 for elt in self: 00285 elt.substitute(subst_dict) 00286 00287 00288 00289 00290 # -------------------------------------------------------------------------- 00291 ## 00292 # @brief True if the object can be displayed as a single 1 00293 # For instance, the Product 1×1×1×1 or the Sum 0 + 0 + 1 + 0 00294 def is_displ_as_a_single_1(self): 00295 raise error.MethodShouldBeRedefined(self, 00296 'is_displ_as_a_single_1') 00297 00298 00299 00300 00301 00302 # -------------------------------------------------------------------------- 00303 ## 00304 # @brief True if the object can be displayed as a single int 00305 def is_displ_as_a_single_int(self): 00306 raise error.MethodShouldBeRedefined(self, 00307 'is_displ_as_a_single_1') 00308 00309 00310 00311 00312 00313 # -------------------------------------------------------------------------- 00314 ## 00315 # @brief True if the object can be displayed as a single -1 00316 # For instance, the Product 1×1×(-1)×1 or the Sum 0 + 0 - 1 + 0 00317 def is_displ_as_a_single_minus_1(self): 00318 raise error.MethodShouldBeRedefined(self, 00319 'is_displ_as_a_single_minus_1') 00320 00321 00322 00323 00324 00325 # -------------------------------------------------------------------------- 00326 ## 00327 # @brief True if the object can be displayed as a single 0 00328 # For instance, the Product 0×0×0×0 (but not 0×1) 00329 # or the Sum 0 + 0 + 0 (but not 0 + 1 - 1) 00330 def is_displ_as_a_single_0(self): 00331 raise error.MethodShouldBeRedefined(self, 00332 'is_displ_as_a_single_0') 00333 00334 00335 00336 00337 00338 # -------------------------------------------------------------------------- 00339 ## 00340 # @brief True if the object is or only contains one numeric Item 00341 def is_displ_as_a_single_numeric_Item(self): 00342 raise error.MethodShouldBeRedefined(self, 00343 'is_displ_as_a_single_numeric_Item') 00344 00345 00346 00347 00348 00349 # -------------------------------------------------------------------------- 00350 ## 00351 # @brief True if the object can be considered as a neutral element 00352 def is_displ_as_a_single_neutral(self, elt): 00353 raise error.MethodShouldBeRedefined(self, 00354 'is_displ_as_a_single_neutral') 00355 00356 00357 00358 00359 00360 # ------------------------------------------------------------------------------ 00361 # -------------------------------------------------------------------------- 00362 # ------------------------------------------------------------------------------ 00363 ## 00364 # @class Signed 00365 # @brief Signed objects: CommutativeOperations (Sums&Products), Items, Quotients... 00366 # Any Signed must have a sign field 00367 class Signed(Calculable): 00368 00369 00370 00371 # -------------------------------------------------------------------------- 00372 ## 00373 # @brief Constructor 00374 # @return A Signed, though it can't really be used as is 00375 def __init__(self): 00376 self._sign = '+' 00377 00378 00379 00380 00381 00382 # -------------------------------------------------------------------------- 00383 ## 00384 # @brief Returns the number of minus signs in the object 00385 def get_minus_signs_nb(self): 00386 raise error.MethodShouldBeRedefined(self, 'get_minus_signs_nb') 00387 00388 00389 00390 00391 00392 # -------------------------------------------------------------------------- 00393 ## 00394 # @brief Returns the sign of the object 00395 def get_sign(self): 00396 return self._sign 00397 # -------------------------------------------------------------------------- 00398 sign = property(get_sign, 00399 doc = "Sign of the object") 00400 00401 00402 00403 00404 00405 # -------------------------------------------------------------------------- 00406 ## 00407 # @brief Set the sign of the object 00408 # @param arg String being '+' or '-' or number being +1 or -1 00409 # @warning Relays an exception if arg is not of the types described 00410 def set_sign(self, arg): 00411 if is_.a_sign(arg): 00412 self._sign = arg 00413 elif arg == 1: 00414 self._sign = '+' 00415 elif arg == -1: 00416 self._sign = '-' 00417 elif isinstance(arg, Calculable): 00418 if arg.is_displ_as_a_single_1(): 00419 self._sign = '+' 00420 elif arg.is_displ_as_a_single_minus_1(): 00421 self._sign = '-' 00422 else: 00423 raise error.UncompatibleType(self, "'+' or '-' or 1 or -1") 00424 00425 00426 00427 00428 00429 # -------------------------------------------------------------------------- 00430 ## 00431 # @brief Changes the sign of the object 00432 def set_opposite_sign(self): 00433 if self.get_sign() == '-': 00434 self.set_sign('+') 00435 elif self.get_sign() == '+': 00436 self.set_sign('-') 00437 else: 00438 # this case should never happen, just to secure the code 00439 raise error.WrongObject("The sign of the object " \ 00440 + self.dbg_str() \ 00441 + " is " \ 00442 + str(self.sign) \ 00443 + " instead of '+' or '-'.") 00444 00445 00446 00447 00448 00449 # -------------------------------------------------------------------------- 00450 ## 00451 # @brief True if object's *sign* is '-' (ie -(-1) would be "negative") 00452 def is_negative(self): 00453 return self.sign == '-' 00454 00455 00456 00457 00458 00459 00460 # -------------------------------------------------------------------------- 00461 ## 00462 # @brief True if object's *sign* is '+' 00463 def is_positive(self): 00464 return self.sign == '+' 00465 00466 00467 00468 00469 00470 00471 # ------------------------------------------------------------------------------ 00472 # -------------------------------------------------------------------------- 00473 # ------------------------------------------------------------------------------ 00474 ## 00475 # @class Value 00476 # @brief This class embedds Numbers & Strings into a basic object. It doesn't 00477 # have any exponent field (always set to 1), so does not belong to 00478 # Exponenteds. This is the only place where numbers are used directly. 00479 # The Item class for instance, contains Values in its fields, not 00480 # numbers. 00481 # This to be sure any content of any field (even if only a simple 00482 # number is to be saved in the field) can be tested & managed 00483 # as an object in any other class than Value. 00484 # Up from 2010/11/19, it is decided that all numeric Values will contain 00485 # a Decimal.decimal number. 00486 class Value(Signed): 00487 00488 00489 00490 00491 00492 # -------------------------------------------------------------------------- 00493 ## 00494 # @brief Constructor 00495 # @warning Might raise an UncompatibleType exception 00496 # or InvalidOperation 00497 # @param arg Number|String 00498 # If the argument is not of one of these kinds, an exception 00499 # will be raised. 00500 # @return One instance of Value 00501 def __init__(self, arg, **options): 00502 Signed.__init__(self) 00503 00504 self._has_been_rounded = False 00505 00506 self._unit = "" 00507 00508 if 'unit' in options and options['unit'] in AVAILABLE_UNITS: 00509 self._unit = options['unit'] 00510 00511 if type(arg) == float \ 00512 or type(arg) == int \ 00513 or type(arg) == Decimal: 00514 #___ 00515 self._raw_value = Decimal(str(arg)) 00516 if arg >= 0: 00517 self._sign = '+' 00518 else: 00519 self._sign = '-' 00520 00521 elif type(arg) == str: 00522 if is_.a_numerical_string(arg): 00523 self._raw_value = Decimal(arg) 00524 else: 00525 self._raw_value = arg 00526 00527 if len(arg) >= 1 and arg[0] == '-': 00528 self._sign = '-' 00529 00530 elif isinstance(arg, Value): 00531 self._raw_value = arg.raw_value 00532 self._has_been_rounded = arg.has_been_rounded 00533 self._unit = arg.unit 00534 00535 # All other unforeseen cases : an exception is raised. 00536 else: 00537 raise error.UncompatibleType(arg, "Number|String") 00538 00539 00540 00541 00542 00543 # -------------------------------------------------------------------------- 00544 ## 00545 # @brief If the object is literal, returns the value 00546 def get_first_letter(self): 00547 if self.is_literal(): 00548 return self.raw_value 00549 else: 00550 raise error.UncompatibleType(self, "str, i.e. literal Value") 00551 # -------------------------------------------------------------------------- 00552 ## 00553 # @brief Returns the "has been rounded" state of the Value 00554 def get_has_been_rounded(self): 00555 return self._has_been_rounded 00556 # -------------------------------------------------------------------------- 00557 ## 00558 # @brief Returns the list of elements to iter over 00559 def get_iteration_list(self): 00560 return [self.raw_value] 00561 # -------------------------------------------------------------------------- 00562 ## 00563 # @brief Returns the number of minus signs in the object 00564 def get_minus_signs_nb(self): 00565 raise error.MethodShouldBeRedefined(self, 'get_minus_signs_nb') 00566 # -------------------------------------------------------------------------- 00567 ## 00568 # @brief Returns the raw value contained in the Value 00569 def get_raw_value(self): 00570 return self._raw_value 00571 # -------------------------------------------------------------------------- 00572 ## 00573 # @brief Returns the sign of the Value 00574 def get_sign(self): 00575 return self._sign 00576 # -------------------------------------------------------------------------- 00577 ## 00578 # @brief Returns the unit of the Value 00579 def get_unit(self): 00580 return self._unit 00581 00582 00583 00584 00585 00586 00587 00588 00589 00590 00591 00592 raw_value = property(get_raw_value, 00593 doc = "Raw value of the object") 00594 has_been_rounded = property(get_has_been_rounded, 00595 doc = "'has been rounded' state of the Value") 00596 sign = property(get_sign, 00597 doc = "Sign of the Value") 00598 00599 unit = property(get_unit, 00600 doc = "Unit of the Value") 00601 00602 00603 00604 00605 00606 00607 00608 00609 00610 00611 # -------------------------------------------------------------------------- 00612 ## 00613 # @brief Sets the "has been rounded" state of the Value 00614 def set_has_been_rounded(self, arg): 00615 if not arg in [True, False]: 00616 raise error.WrongArgument(str(type(arg)), "True|False") 00617 else: 00618 self._has_been_rounded = arg 00619 00620 00621 00622 00623 00624 00625 00626 00627 00628 # -------------------------------------------------------------------------- 00629 ## 00630 # @brief Set the sign of the object 00631 # @param arg String being '+' or '-' or number being +1 or -1 00632 # @warning Relays an exception if arg is not of the types described 00633 def set_sign(self, arg): 00634 if is_.a_sign(arg): 00635 self._sign = arg 00636 elif arg == 1: 00637 self._sign = '+' 00638 elif arg == -1: 00639 self._sign = '-' 00640 elif isinstance(arg, Calculable): 00641 if arg.is_displ_as_a_single_1(): 00642 self._sign = '+' 00643 elif arg.is_displ_as_a_single_minus_1(): 00644 self._sign = '-' 00645 else: 00646 raise error.UncompatibleType(self, "'+' or '-' or 1 or -1") 00647 00648 00649 00650 00651 00652 # -------------------------------------------------------------------------- 00653 ## 00654 # @brief Set the unit of the Value 00655 # @param arg String 00656 def set_unit(self, arg): 00657 if not type(arg) == str: 00658 raise error.WrongArgument(str(type(arg)), "a str") 00659 else: 00660 self._unit = arg 00661 00662 00663 00664 00665 # -------------------------------------------------------------------------- 00666 ## 00667 # @brief Changes the sign of the object 00668 def set_opposite_sign(self): 00669 if self.get_sign() == '-': 00670 self.set_sign('+') 00671 elif self.get_sign() == '+': 00672 self.set_sign('-') 00673 else: 00674 # this case should never happen, just to secure the code 00675 raise error.WrongObject("The sign of the object " \ 00676 + self.dbg_str() \ 00677 + " is " \ 00678 + str(self.sign) \ 00679 + " instead of '+' or '-'.") 00680 00681 00682 00683 00684 00685 # -------------------------------------------------------------------------- 00686 ## 00687 # @brief Creates a string of the given object in the given ML 00688 # @param options Any options 00689 # @return The formated string 00690 def into_str(self, **options): 00691 00692 if 'display_unit' in options and options['display_unit'] in YES \ 00693 and self.unit != None and self.unit != '': 00694 #___ 00695 unit_str = VALUE_AND_UNIT_SEPARATOR[self.unit] + self.unit 00696 00697 if self.is_numeric(): 00698 if 'display_unit' in options and options['display_unit'] in YES: 00699 if 'graphic_display' in options\ 00700 and options['graphic_display'] in YES: 00701 #___ 00702 return locale.str(self.raw_value)\ 00703 + unit_str 00704 else: 00705 return locale.str(self.raw_value)\ 00706 + MARKUP['open_text_in_maths']\ 00707 + unit_str \ 00708 + MARKUP['close_text_in_maths'] 00709 else: 00710 return locale.str(self.raw_value) 00711 00712 else: # self.is_literal() 00713 if len(self.get_first_letter()) >= 2 \ 00714 and not (self.get_first_letter()[0] == "-" \ 00715 or self.get_first_letter()[0] == "+"): 00716 #___ 00717 return MARKUP['open_text_in_maths'] \ 00718 + str(self.raw_value) \ 00719 + MARKUP['close_text_in_maths'] 00720 else: 00721 return str(self.raw_value) 00722 00723 00724 00725 00726 00727 # -------------------------------------------------------------------------- 00728 ## 00729 # @brief Returns the value of a numeric Value 00730 # @warning Raise an exception if not numeric 00731 def evaluate(self): 00732 if not self.is_numeric(): 00733 raise error.UncompatibleType(self, "numeric Value") 00734 else: 00735 return self.raw_value 00736 00737 00738 00739 00740 00741 00742 # -------------------------------------------------------------------------- 00743 ## 00744 # @brief Returns None 00745 def calculate_next_step(self, **options): 00746 return None 00747 00748 00749 00750 00751 00752 # -------------------------------------------------------------------------- 00753 ## 00754 # @brief Debugging method to print the Value 00755 def dbg_str(self, **options): 00756 return "." + str(self.raw_value) + "." 00757 00758 00759 00760 00761 00762 # -------------------------------------------------------------------------- 00763 ## 00764 # @brief Compares two Values 00765 # @todo check if __eq__ shouldn't return +1 if value of self > objct 00766 # @todo comparison directly with numbers... (see alphabetical_order_cmp) 00767 # @return True if they're equal 00768 def __eq__(self, other_value): 00769 if not isinstance(other_value, Value): 00770 return False 00771 00772 if self.raw_value == other_value.raw_value: 00773 return True 00774 else: 00775 return False 00776 00777 00778 00779 00780 00781 # -------------------------------------------------------------------------- 00782 ## 00783 # @brief Returns the Value's length 00784 # @return 1 00785 def __len__(self): 00786 return 1 00787 00788 00789 00790 00791 # -------------------------------------------------------------------------- 00792 ## 00793 # @brief Makes Values hashable (so, usable as dictionnary keys) 00794 def __hash__(self): 00795 return hash(str(self.sign) + str(self.raw_value) \ 00796 + str(self.has_been_rounded) + str(self.unit) 00797 ) 00798 00799 00800 00801 00802 00803 # -------------------------------------------------------------------------- 00804 ## 00805 # @brief Executes the multiplication with another object 00806 # @warning Will raise an error if you try to multiply a literal 00807 # with a number 00808 def __mul__(self, objct): 00809 if isinstance(objct, Calculable): 00810 return self.raw_value * objct.evaluate() 00811 else: 00812 return self.raw_value * objct 00813 00814 00815 00816 00817 00818 # -------------------------------------------------------------------------- 00819 ## 00820 # @brief Executes the addition with another object 00821 # @warning Will raise an error if you try to add a literal with a number 00822 def __add__(self, objct): 00823 if isinstance(objct, Calculable): 00824 return self.raw_value + objct.evaluate() 00825 else: 00826 return self.raw_value + objct 00827 00828 00829 00830 00831 00832 # -------------------------------------------------------------------------- 00833 ## 00834 # @brief Uses the given lexicon to substitute literal Values in self 00835 def substitute(self, subst_dict): 00836 if self.is_literal(): 00837 for key in subst_dict: 00838 if self == key: 00839 self.__init__(subst_dict[key]) 00840 #done = True 00841 00842 #if not done: 00843 # raise error.ImpossibleAction("substitute because the numeric "\ 00844 # + "value matching the literal here is not in the "\ 00845 # + "substitution dictionnary") 00846 00847 else: 00848 pass 00849 00850 00851 00852 00853 00854 # -------------------------------------------------------------------------- 00855 ## 00856 # @brief Returns a Value containing the square root of self 00857 def sqrt(self): 00858 if self.is_numeric(): 00859 return Value(self.raw_value.sqrt()) 00860 else: 00861 raise error.UncompatibleType(self, "numeric Value") 00862 00863 00864 00865 # -------------------------------------------------------------------------- 00866 ## 00867 # @brief Returns the value once rounded to the given precision 00868 def round(self, precision): 00869 if not self.is_numeric(): 00870 raise error.UncompatibleType(self, "numeric Value") 00871 elif not (precision in [UNIT, 00872 TENTH, 00873 HUNDREDTH, 00874 THOUSANDTH, 00875 TEN_THOUSANDTH] \ 00876 or (type(precision) == int and precision >= 0 and precision <= 4)): 00877 #___ 00878 raise error.UncompatibleType(precision, "must be UNIT or" \ 00879 + "TENTH, " \ 00880 + "HUNDREDTH, " \ 00881 + "THOUSANDTH, " \ 00882 + "TEN_THOUSANDTH, "\ 00883 + "or 0, 1, 2, 3 or 4.") 00884 else: 00885 result_value = None 00886 00887 if type(precision) == int: 00888 result_value = Value(round(self.raw_value, 00889 Decimal(PRECISION[precision]), 00890 rounding=ROUND_HALF_UP 00891 ) 00892 ) 00893 else: 00894 result_value = Value(round(self.raw_value, 00895 Decimal(precision), 00896 rounding=ROUND_HALF_UP 00897 ) 00898 ) 00899 00900 if self.needs_to_get_rounded(precision): 00901 result_value.set_has_been_rounded(True) 00902 00903 return result_value 00904 00905 00906 00907 00908 # -------------------------------------------------------------------------- 00909 ## 00910 # @brief Returns the number of digits of a numerical value 00911 def digits_number(self): 00912 if not self.is_numeric(): 00913 raise error.UncompatibleType(self, "numeric Value") 00914 else: 00915 temp_result = len(str((self.raw_value \ 00916 - round(self.raw_value, 00917 Decimal(UNIT), 00918 rounding=ROUND_DOWN 00919 ) 00920 )) 00921 ) \ 00922 - 2 00923 00924 if temp_result < 0: 00925 return 0 00926 else: 00927 return temp_result 00928 00929 00930 00931 00932 00933 # -------------------------------------------------------------------------- 00934 ## 00935 # @brief Returns True/False depending on the need of the value to get 00936 # rounded (for instance 2.68 doesn't need to get rounded if 00937 # precision is HUNDREDTH or more, but needs it if it is less) 00938 def needs_to_get_rounded(self, precision): 00939 if not (precision in [UNIT, 00940 TENTH, 00941 HUNDREDTH, 00942 THOUSANDTH, 00943 TEN_THOUSANDTH] \ 00944 or (type(precision) == int and precision >= 0 and precision <= 4)): 00945 #___ 00946 raise error.UncompatibleType(precision, "must be UNIT or" \ 00947 + "TENTH, " \ 00948 + "HUNDREDTH, " \ 00949 + "THOUSANDTH, " \ 00950 + "TEN_THOUSANDTH, "\ 00951 + "or 0, 1, 2, 3 or 4.") 00952 00953 precision_to_test = 0 00954 00955 if type(precision) == int: 00956 precision_to_test = precision 00957 else: 00958 precision_to_test = PRECISION_REVERSED[precision] 00959 00960 return self.digits_number() > precision_to_test 00961 00962 00963 00964 00965 00966 # -------------------------------------------------------------------------- 00967 ## 00968 # @brief To check if this contains a rounded number... 00969 # @return True or False depending on the Value inside 00970 def contains_a_rounded_number(self): 00971 return self.has_been_rounded 00972 00973 00974 00975 00976 00977 00978 # -------------------------------------------------------------------------- 00979 ## 00980 # @brief Always False for a Value 00981 # @param objct The object to search for 00982 # @return False 00983 def contains_exactly(self, objct): 00984 return False 00985 00986 00987 00988 00989 00990 00991 # -------------------------------------------------------------------------- 00992 ## 00993 # @brief True if the object contains a perfect square (integer or decimal) 00994 def is_a_perfect_square(self): 00995 if not self.is_numeric(): 00996 raise error.UncompatibleType(self, "numeric Value") 00997 00998 if self.is_an_integer(): 00999 return not self.sqrt().needs_to_get_rounded(0) 01000 else: 01001 return len(str(self.raw_value)) > len(str(self.raw_value.sqrt())) 01002 01003 01004 01005 01006 # -------------------------------------------------------------------------- 01007 ## 01008 # @brief True if the object contains an integer (numeric) 01009 def is_an_integer(self): 01010 if not self.is_numeric(): 01011 raise error.UncompatibleType(self, "numeric Value") 01012 01013 getcontext().clear_flags() 01014 01015 trash = self.raw_value.to_integral_exact() 01016 01017 return getcontext().flags[Rounded] == 0 01018 01019 01020 01021 01022 # -------------------------------------------------------------------------- 01023 ## 01024 # @brief True if the object only contains numeric objects 01025 def is_numeric(self): 01026 if type(self.raw_value) == float \ 01027 or type(self.raw_value) == int \ 01028 or type(self.raw_value) == Decimal: 01029 #___ 01030 return True 01031 else: 01032 return False 01033 01034 01035 01036 01037 01038 # -------------------------------------------------------------------------- 01039 ## 01040 # @brief True if the object only contains literal objects 01041 def is_literal(self): 01042 if type(self.raw_value) == str: 01043 #___ 01044 return True 01045 else: 01046 return False 01047 01048 01049 01050 01051 01052 # -------------------------------------------------------------------------- 01053 ## 01054 # @brief True if the evaluated value of an object is null 01055 def is_null(self): 01056 if self.is_numeric() and self.raw_value == 0: 01057 return True 01058 else: 01059 return False 01060 01061 01062 01063 01064 01065 # -------------------------------------------------------------------------- 01066 ## 01067 # @brief True if the object can be displayed as a single 1 01068 def is_displ_as_a_single_1(self): 01069 if self.is_numeric() and self.raw_value == 1: 01070 return True 01071 else: 01072 return False 01073 01074 01075 01076 01077 01078 # -------------------------------------------------------------------------- 01079 ## 01080 # @brief True if the object can be displayed as a single -1 01081 def is_displ_as_a_single_minus_1(self): 01082 if self.is_numeric() and self.raw_value == -1: 01083 return True 01084 else: 01085 return False 01086 01087 01088 01089 01090 01091 # -------------------------------------------------------------------------- 01092 ## 01093 # @brief True if the object can be displayed as a single 0 01094 def is_displ_as_a_single_0(self): 01095 if self.is_numeric() and self.raw_value == 0: 01096 return True 01097 else: 01098 return False 01099 01100 01101 01102 01103 01104 # -------------------------------------------------------------------------- 01105 ## 01106 # @brief True if the object is or only contains one numeric Item 01107 def is_displ_as_a_single_numeric_Item(self): 01108 return False 01109 01110 01111 01112 01113 01114 # -------------------------------------------------------------------------- 01115 ## 01116 # @brief True if the object can be displayed as a single int 01117 def is_displ_as_a_single_int(self): 01118 return self.is_numeric() and self.is_an_integer() 01119 01120 01121 01122 01123 # ------------------------------------------------------------------------------ 01124 # -------------------------------------------------------------------------- 01125 # ------------------------------------------------------------------------------ 01126 ## 01127 # @class Exponented 01128 # @brief Exponented objects: CommutativeOperations (Sums&Products), Items, Quotients... 01129 # Any Exponented must have a exponent field and should reimplement the 01130 # methods that are not already defined hereafter 01131 class Exponented(Signed): 01132 01133 01134 01135 01136 01137 # -------------------------------------------------------------------------- 01138 ## 01139 # @brief Constructor 01140 # @return An Exponented, though it can't really be used as is 01141 def __init__(self): 01142 Signed.__init__(self) 01143 self._exponent = Value(1) 01144 01145 01146 01147 01148 01149 # -------------------------------------------------------------------------- 01150 ## 01151 # @brief Gets the exponent of the Function 01152 # @brief this should be already done by Item.get_exponent()... 01153 def get_exponent(self): 01154 return self._exponent 01155 # -------------------------------------------------------------------------- 01156 exponent = property(get_exponent, doc = "Exponent of the Function") 01157 01158 01159 01160 01161 01162 # -------------------------------------------------------------------------- 01163 ## 01164 # @brief Set the value of the exponent 01165 # @param arg Calculable|Number|String 01166 # @warning Relays an exception if arg is not of the types described 01167 def set_exponent(self, arg): 01168 if isinstance(arg, Calculable): 01169 self._exponent = arg.clone() 01170 else: 01171 self._exponent = Value(arg) 01172 01173 01174 01175 01176 01177 # -------------------------------------------------------------------------- 01178 ## 01179 # @brief True if the exponent isn't equivalent to a single 1 01180 # @return True if the exponent is not equivalent to a single 1 01181 def exponent_must_be_displayed(self): 01182 if not self.exponent.is_displ_as_a_single_1(): 01183 return True 01184 else: 01185 return False 01186 01187 01188 01189 01190 01191 01192 01193 01194 01195