mathmaker
0.4(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 self.get_iteration_list().next() 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) == long \ 00514 or type(arg) == Decimal: 00515 #___ 00516 self._raw_value = Decimal(str(arg)) 00517 if arg >= 0: 00518 self._sign = '+' 00519 else: 00520 self._sign = '-' 00521 00522 elif type(arg) == str: 00523 if is_.a_numerical_string(arg): 00524 self._raw_value = Decimal(arg) 00525 else: 00526 self._raw_value = arg 00527 00528 if len(arg) >= 1 and arg[0] == '-': 00529 self._sign = '-' 00530 00531 elif isinstance(arg, Value): 00532 self._raw_value = arg.raw_value 00533 self._has_been_rounded = arg.has_been_rounded 00534 self._unit = arg.unit 00535 00536 # All other unforeseen cases : an exception is raised. 00537 else: 00538 raise error.UncompatibleType(arg, "Number|String") 00539 00540 00541 00542 00543 00544 # -------------------------------------------------------------------------- 00545 ## 00546 # @brief If the object is literal, returns the value 00547 def get_first_letter(self): 00548 if self.is_literal(): 00549 return self.raw_value 00550 else: 00551 raise error.UncompatibleType(self, "str, i.e. literal Value") 00552 # -------------------------------------------------------------------------- 00553 ## 00554 # @brief Returns the "has been rounded" state of the Value 00555 def get_has_been_rounded(self): 00556 return self._has_been_rounded 00557 # -------------------------------------------------------------------------- 00558 ## 00559 # @brief Returns the list of elements to iter over 00560 def get_iteration_list(self): 00561 return [self.raw_value] 00562 # -------------------------------------------------------------------------- 00563 ## 00564 # @brief Returns the number of minus signs in the object 00565 def get_minus_signs_nb(self): 00566 raise error.MethodShouldBeRedefined(self, 'get_minus_signs_nb') 00567 # -------------------------------------------------------------------------- 00568 ## 00569 # @brief Returns the raw value contained in the Value 00570 def get_raw_value(self): 00571 return self._raw_value 00572 # -------------------------------------------------------------------------- 00573 ## 00574 # @brief Returns the sign of the Value 00575 def get_sign(self): 00576 return self._sign 00577 # -------------------------------------------------------------------------- 00578 ## 00579 # @brief Returns the unit of the Value 00580 def get_unit(self): 00581 return self._unit 00582 00583 00584 00585 00586 00587 00588 00589 00590 00591 00592 00593 raw_value = property(get_raw_value, 00594 doc = "Raw value of the object") 00595 has_been_rounded = property(get_has_been_rounded, 00596 doc = "'has been rounded' state of the Value") 00597 sign = property(get_sign, 00598 doc = "Sign of the Value") 00599 00600 unit = property(get_unit, 00601 doc = "Unit of the Value") 00602 00603 00604 00605 00606 00607 00608 00609 00610 00611 00612 # -------------------------------------------------------------------------- 00613 ## 00614 # @brief Sets the "has been rounded" state of the Value 00615 def set_has_been_rounded(self, arg): 00616 if not arg in [True, False]: 00617 raise error.WrongArgument(str(type(arg)), "True|False") 00618 else: 00619 self._has_been_rounded = arg 00620 00621 00622 00623 00624 00625 00626 00627 00628 00629 # -------------------------------------------------------------------------- 00630 ## 00631 # @brief Set the sign of the object 00632 # @param arg String being '+' or '-' or number being +1 or -1 00633 # @warning Relays an exception if arg is not of the types described 00634 def set_sign(self, arg): 00635 if is_.a_sign(arg): 00636 self._sign = arg 00637 elif arg == 1: 00638 self._sign = '+' 00639 elif arg == -1: 00640 self._sign = '-' 00641 elif isinstance(arg, Calculable): 00642 if arg.is_displ_as_a_single_1(): 00643 self._sign = '+' 00644 elif arg.is_displ_as_a_single_minus_1(): 00645 self._sign = '-' 00646 else: 00647 raise error.UncompatibleType(self, "'+' or '-' or 1 or -1") 00648 00649 00650 00651 00652 00653 # -------------------------------------------------------------------------- 00654 ## 00655 # @brief Set the unit of the Value 00656 # @param arg String 00657 def set_unit(self, arg): 00658 if not type(arg) == str: 00659 raise error.WrongArgument(str(type(arg)), "a str") 00660 else: 00661 self._unit = arg 00662 00663 00664 00665 00666 # -------------------------------------------------------------------------- 00667 ## 00668 # @brief Changes the sign of the object 00669 def set_opposite_sign(self): 00670 if self.get_sign() == '-': 00671 self.set_sign('+') 00672 elif self.get_sign() == '+': 00673 self.set_sign('-') 00674 else: 00675 # this case should never happen, just to secure the code 00676 raise error.WrongObject("The sign of the object " \ 00677 + self.dbg_str() \ 00678 + " is " \ 00679 + str(self.sign) \ 00680 + " instead of '+' or '-'.") 00681 00682 00683 00684 00685 00686 # -------------------------------------------------------------------------- 00687 ## 00688 # @brief Creates a string of the given object in the given ML 00689 # @param options Any options 00690 # @return The formated string 00691 def into_str(self, **options): 00692 00693 if 'display_unit' in options and options['display_unit'] in YES \ 00694 and self.unit != None and self.unit != '': 00695 #___ 00696 unit_str = VALUE_AND_UNIT_SEPARATOR[self.unit] + self.unit 00697 00698 if self.is_numeric(): 00699 if 'display_unit' in options and options['display_unit'] in YES: 00700 if 'graphic_display' in options\ 00701 and options['graphic_display'] in YES: 00702 #___ 00703 return locale.str(self.raw_value)\ 00704 + unit_str 00705 else: 00706 return locale.str(self.raw_value)\ 00707 + MARKUP['open_text_in_maths']\ 00708 + unit_str \ 00709 + MARKUP['close_text_in_maths'] 00710 else: 00711 return locale.str(self.raw_value) 00712 00713 else: # self.is_literal() 00714 if len(self.get_first_letter()) >= 2 \ 00715 and not (self.get_first_letter()[0] == "-" \ 00716 or self.get_first_letter()[0] == "+"): 00717 #___ 00718 return MARKUP['open_text_in_maths'] \ 00719 + str(self.raw_value) \ 00720 + MARKUP['close_text_in_maths'] 00721 else: 00722 return str(self.raw_value) 00723 00724 00725 00726 00727 00728 # -------------------------------------------------------------------------- 00729 ## 00730 # @brief Returns the value of a numeric Value 00731 # @warning Raise an exception if not numeric 00732 def evaluate(self): 00733 if not self.is_numeric(): 00734 raise error.UncompatibleType(self, "numeric Value") 00735 else: 00736 return self.raw_value 00737 00738 00739 00740 00741 00742 00743 # -------------------------------------------------------------------------- 00744 ## 00745 # @brief Returns None 00746 def calculate_next_step(self, **options): 00747 return None 00748 00749 00750 00751 00752 00753 # -------------------------------------------------------------------------- 00754 ## 00755 # @brief Debugging method to print the Value 00756 def dbg_str(self, **options): 00757 return "." + str(self.raw_value) + "." 00758 00759 00760 00761 00762 00763 # -------------------------------------------------------------------------- 00764 ## 00765 # @brief Compares two Values 00766 # @todo check if __cmp__ shouldn't return +1 if value of self > objct 00767 # @todo comparison directly with numbers... (see alphabetical_order_cmp) 00768 # @return -1|0 (i.e. they're equal) 00769 def __cmp__(self, other_value): 00770 if not isinstance(other_value, Value): 00771 return -1 00772 00773 if self.raw_value == other_value.raw_value: 00774 return 0 00775 else: 00776 return -1 00777 00778 00779 00780 00781 00782 # -------------------------------------------------------------------------- 00783 ## 00784 # @brief Returns the Value's length 00785 # @return 1 00786 def __len__(self): 00787 return 1 00788 00789 00790 00791 00792 # -------------------------------------------------------------------------- 00793 ## 00794 # @brief Executes the multiplication with another object 00795 # @warning Will raise an error if you try to multiply a literal 00796 # with a number 00797 def __mul__(self, objct): 00798 if isinstance(objct, Calculable): 00799 return self.raw_value * objct.evaluate() 00800 else: 00801 return self.raw_value * objct 00802 00803 00804 00805 00806 00807 # -------------------------------------------------------------------------- 00808 ## 00809 # @brief Executes the addition with another object 00810 # @warning Will raise an error if you try to add a literal with a number 00811 def __add__(self, objct): 00812 if isinstance(objct, Calculable): 00813 return self.raw_value + objct.evaluate() 00814 else: 00815 return self.raw_value + objct 00816 00817 00818 00819 00820 00821 # -------------------------------------------------------------------------- 00822 ## 00823 # @brief Uses the given lexicon to substitute literal Values in self 00824 def substitute(self, subst_dict): 00825 if self.is_literal(): 00826 for key in subst_dict: 00827 if self == key: 00828 self.__init__(subst_dict[key]) 00829 #done = True 00830 00831 #if not done: 00832 # raise error.ImpossibleAction("substitute because the numeric "\ 00833 # + "value matching the literal here is not in the "\ 00834 # + "substitution dictionnary") 00835 00836 else: 00837 pass 00838 00839 00840 00841 00842 00843 # -------------------------------------------------------------------------- 00844 ## 00845 # @brief Returns a Value containing the square root of self 00846 def sqrt(self): 00847 if self.is_numeric(): 00848 return Value(self.raw_value.sqrt()) 00849 else: 00850 raise error.UncompatibleType(self, "numeric Value") 00851 00852 00853 00854 # -------------------------------------------------------------------------- 00855 ## 00856 # @brief Returns the value once rounded to the given precision 00857 def round(self, precision): 00858 if not self.is_numeric(): 00859 raise error.UncompatibleType(self, "numeric Value") 00860 elif not (precision in [UNIT, 00861 TENTH, 00862 HUNDREDTH, 00863 THOUSANDTH, 00864 TEN_THOUSANDTH] \ 00865 or (type(precision) == int and precision >= 0 and precision <= 4)): 00866 #___ 00867 raise error.UncompatibleType(precision, "must be UNIT or" \ 00868 + "TENTH, " \ 00869 + "HUNDREDTH, " \ 00870 + "THOUSANDTH, " \ 00871 + "TEN_THOUSANDTH, "\ 00872 + "or 0, 1, 2, 3 or 4.") 00873 else: 00874 result_value = None 00875 00876 if type(precision) == int: 00877 result_value = Value(round(self.raw_value, 00878 Decimal(PRECISION[precision]), 00879 rounding=ROUND_HALF_UP 00880 ) 00881 ) 00882 else: 00883 result_value = Value(round(self.raw_value, 00884 Decimal(precision), 00885 rounding=ROUND_HALF_UP 00886 ) 00887 ) 00888 00889 if self.needs_to_get_rounded(precision): 00890 result_value.set_has_been_rounded(True) 00891 00892 return result_value 00893 00894 00895 00896 00897 # -------------------------------------------------------------------------- 00898 ## 00899 # @brief Returns the number of digits of a numerical value 00900 def digits_number(self): 00901 if not self.is_numeric(): 00902 raise error.UncompatibleType(self, "numeric Value") 00903 else: 00904 temp_result = len(str((self.raw_value \ 00905 - round(self.raw_value, 00906 Decimal(UNIT), 00907 rounding=ROUND_DOWN 00908 ) 00909 )) 00910 ) \ 00911 - 2 00912 00913 if temp_result < 0: 00914 return 0 00915 else: 00916 return temp_result 00917 00918 00919 00920 00921 00922 # -------------------------------------------------------------------------- 00923 ## 00924 # @brief Returns True/False depending on the need of the value to get 00925 # rounded (for instance 2.68 doesn't need to get rounded if 00926 # precision is HUNDREDTH or more, but needs it if it is less) 00927 def needs_to_get_rounded(self, precision): 00928 if not (precision in [UNIT, 00929 TENTH, 00930 HUNDREDTH, 00931 THOUSANDTH, 00932 TEN_THOUSANDTH] \ 00933 or (type(precision) == int and precision >= 0 and precision <= 4)): 00934 #___ 00935 raise error.UncompatibleType(precision, "must be UNIT or" \ 00936 + "TENTH, " \ 00937 + "HUNDREDTH, " \ 00938 + "THOUSANDTH, " \ 00939 + "TEN_THOUSANDTH, "\ 00940 + "or 0, 1, 2, 3 or 4.") 00941 00942 precision_to_test = 0 00943 00944 if type(precision) == int: 00945 precision_to_test = precision 00946 else: 00947 precision_to_test = PRECISION_REVERSED[precision] 00948 00949 return self.digits_number() > precision_to_test 00950 00951 00952 00953 00954 00955 # -------------------------------------------------------------------------- 00956 ## 00957 # @brief To check if this contains a rounded number... 00958 # @return True or False depending on the Value inside 00959 def contains_a_rounded_number(self): 00960 return self.has_been_rounded 00961 00962 00963 00964 00965 00966 00967 # -------------------------------------------------------------------------- 00968 ## 00969 # @brief Always False for a Value 00970 # @param objct The object to search for 00971 # @return False 00972 def contains_exactly(self, objct): 00973 return False 00974 00975 00976 00977 00978 00979 00980 # -------------------------------------------------------------------------- 00981 ## 00982 # @brief True if the object contains a perfect square (integer or decimal) 00983 def is_a_perfect_square(self): 00984 if not self.is_numeric(): 00985 raise error.UncompatibleType(self, "numeric Value") 00986 00987 if self.is_an_integer(): 00988 return not self.sqrt().needs_to_get_rounded(0) 00989 else: 00990 return len(str(self.raw_value)) > len(str(self.raw_value.sqrt())) 00991 00992 00993 00994 00995 # -------------------------------------------------------------------------- 00996 ## 00997 # @brief True if the object contains an integer (numeric) 00998 def is_an_integer(self): 00999 if not self.is_numeric(): 01000 raise error.UncompatibleType(self, "numeric Value") 01001 01002 getcontext().clear_flags() 01003 01004 trash = self.raw_value.to_integral_exact() 01005 01006 return getcontext().flags[Rounded] == 0 01007 01008 01009 01010 01011 # -------------------------------------------------------------------------- 01012 ## 01013 # @brief True if the object only contains numeric objects 01014 def is_numeric(self): 01015 if type(self.raw_value) == float \ 01016 or type(self.raw_value) == int \ 01017 or type(self.raw_value) == long \ 01018 or type(self.raw_value) == Decimal: 01019 #___ 01020 return True 01021 else: 01022 return False 01023 01024 01025 01026 01027 01028 # -------------------------------------------------------------------------- 01029 ## 01030 # @brief True if the object only contains literal objects 01031 def is_literal(self): 01032 if type(self.raw_value) == str: 01033 #___ 01034 return True 01035 else: 01036 return False 01037 01038 01039 01040 01041 01042 # -------------------------------------------------------------------------- 01043 ## 01044 # @brief True if the evaluated value of an object is null 01045 def is_null(self): 01046 if self.is_numeric() and self.raw_value == 0: 01047 return True 01048 else: 01049 return False 01050 01051 01052 01053 01054 01055 # -------------------------------------------------------------------------- 01056 ## 01057 # @brief True if the object can be displayed as a single 1 01058 def is_displ_as_a_single_1(self): 01059 if self.is_numeric() and self.raw_value == 1: 01060 return True 01061 else: 01062 return False 01063 01064 01065 01066 01067 01068 # -------------------------------------------------------------------------- 01069 ## 01070 # @brief True if the object can be displayed as a single -1 01071 def is_displ_as_a_single_minus_1(self): 01072 if self.is_numeric() and self.raw_value == -1: 01073 return True 01074 else: 01075 return False 01076 01077 01078 01079 01080 01081 # -------------------------------------------------------------------------- 01082 ## 01083 # @brief True if the object can be displayed as a single 0 01084 def is_displ_as_a_single_0(self): 01085 if self.is_numeric() and self.raw_value == 0: 01086 return True 01087 else: 01088 return False 01089 01090 01091 01092 01093 01094 # -------------------------------------------------------------------------- 01095 ## 01096 # @brief True if the object is or only contains one numeric Item 01097 def is_displ_as_a_single_numeric_Item(self): 01098 return False 01099 01100 01101 01102 01103 01104 # -------------------------------------------------------------------------- 01105 ## 01106 # @brief True if the object can be displayed as a single int 01107 def is_displ_as_a_single_int(self): 01108 return self.is_numeric() and self.is_an_integer() 01109 01110 01111 01112 01113 # ------------------------------------------------------------------------------ 01114 # -------------------------------------------------------------------------- 01115 # ------------------------------------------------------------------------------ 01116 ## 01117 # @class Exponented 01118 # @brief Exponented objects: CommutativeOperations (Sums&Products), Items, Quotients... 01119 # Any Exponented must have a exponent field and should reimplement the 01120 # methods that are not already defined hereafter 01121 class Exponented(Signed): 01122 01123 01124 01125 01126 01127 # -------------------------------------------------------------------------- 01128 ## 01129 # @brief Constructor 01130 # @return An Exponented, though it can't really be used as is 01131 def __init__(self): 01132 Signed.__init__(self) 01133 self._exponent = Value(1) 01134 01135 01136 01137 01138 01139 # -------------------------------------------------------------------------- 01140 ## 01141 # @brief Gets the exponent of the Function 01142 # @brief this should be already done by Item.get_exponent()... 01143 def get_exponent(self): 01144 return self._exponent 01145 # -------------------------------------------------------------------------- 01146 exponent = property(get_exponent, doc = "Exponent of the Function") 01147 01148 01149 01150 01151 01152 # -------------------------------------------------------------------------- 01153 ## 01154 # @brief Set the value of the exponent 01155 # @param arg Calculable|Number|String 01156 # @warning Relays an exception if arg is not of the types described 01157 def set_exponent(self, arg): 01158 if isinstance(arg, Calculable): 01159 self._exponent = arg.clone() 01160 else: 01161 self._exponent = Value(arg) 01162 01163 01164 01165 01166 01167 # -------------------------------------------------------------------------- 01168 ## 01169 # @brief True if the exponent isn't equivalent to a single 1 01170 # @return True if the exponent is not equivalent to a single 1 01171 def exponent_must_be_displayed(self): 01172 if not self.exponent.is_displ_as_a_single_1(): 01173 return True 01174 else: 01175 return False 01176 01177 01178 01179 01180 01181 01182 01183 01184 01185