mathmaker
0.6(alpha)
|
00001 # -*- coding: utf-8 -*- 00002 00003 # Mathmaker creates automatically maths exercises sheets 00004 # with their answers 00005 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net> 00006 00007 # This file is part of Mathmaker. 00008 00009 # Mathmaker is free software; you can redistribute it and/or modify 00010 # it under the terms of the GNU General Public License as published by 00011 # the Free Software Foundation; either version 3 of the License, or 00012 # any later version. 00013 00014 # Mathmaker is distributed in the hope that it will be useful, 00015 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 # GNU General Public License for more details. 00018 00019 # You should have received a copy of the GNU General Public License 00020 # along with Mathmaker; if not, write to the Free Software 00021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00022 00023 import math 00024 import decimal 00025 from lib import * 00026 from lib import translator 00027 from base import * 00028 from base_calculus import * 00029 from lib.common import default 00030 from lib.maths_lib import * 00031 from lib.common.cst import * 00032 from lib.utils import * 00033 00034 markup_choice = cfg.get_value_from_file('MARKUP', 'USE') 00035 00036 if markup_choice == 'latex': 00037 from lib.common.latex import MARKUP 00038 00039 MAX_VALUE = 20 00040 00041 # The following tables come from the fractions' calculations sheets... 00042 # which is a shame : this code must be factorized 00043 FRACTIONS_SUMS_TABLE = [([1, 2], 15), 00044 ([1, 3], 15), 00045 ([1, 4], 15), 00046 ([1, 5], 15), 00047 ([1, 6], 15), 00048 ([1, 7], 15), 00049 ([2, 3], 17), 00050 ([2, 5], 10), 00051 ([2, 7], 7), 00052 ([3, 4], 9), 00053 ([3, 5], 7), 00054 ([3, 7], 5), 00055 ([4, 5], 5), 00056 ([4, 7], 3), 00057 ([5, 6], 4), 00058 ([5, 7], 4), 00059 ([6, 7], 3)] 00060 00061 FRACTIONS_SUMS_SCALE_TABLE = [0.02, #(1, 2) 00062 0.02, 00063 0.02, 00064 0.01, 00065 0.005, 00066 0.005, #(1, 7) 00067 0.21, #(2, 3) 00068 0.14, #(2, 5) 00069 0.02, #(2, 7) 00070 0.21, #(3, 4) 00071 0.14, #(3, 5) 00072 0.01, #(3, 7) 00073 0.16, #(4, 5) 00074 0.01, #(4, 7) 00075 0.01, #(5, 6) 00076 0.005, #(5, 7) 00077 0.005] #(6, 7) 00078 00079 00080 00081 # ------------------------------------------------------------------------------ 00082 # -------------------------------------------------------------------------- 00083 # ------------------------------------------------------------------------------ 00084 ## 00085 # @package core.calculus 00086 00087 00088 00089 00090 00091 00092 00093 00094 00095 00096 00097 # ------------------------------------------------------------------------------ 00098 # -------------------------------------------------------------------------- 00099 # ------------------------------------------------------------------------------ 00100 ## 00101 # @class ComposedCalculable 00102 # @brief Abstract mother class of objects composed of Calculable=Calculable=... 00103 class ComposedCalculable(Printable): 00104 00105 00106 00107 00108 00109 # -------------------------------------------------------------------------- 00110 ## 00111 # @brief Constructor 00112 # @warning Must be redefined 00113 def __init__(self): 00114 raise error.MethodShouldBeRedefined(self, "__init__") 00115 00116 00117 00118 00119 00120 # ------------------------------------------------------------------------------ 00121 # -------------------------------------------------------------------------- 00122 # ------------------------------------------------------------------------------ 00123 ## 00124 # @class Expression 00125 # @brief These are object of the kind : Name = Exponented 00126 class Expression(ComposedCalculable): 00127 00128 00129 00130 00131 00132 # -------------------------------------------------------------------------- 00133 ## 00134 # @brief Constructor 00135 # @warning Might raise an UncompatibleType exception. 00136 # @param integer_or_letter A string or an integer 00137 # @param objct The Exponented that will be at the right hand side 00138 # @return One instance of Expression 00139 def __init__(self, integer_or_letter, objct): 00140 # just check if the given arguments are right 00141 if not (is_.a_string(integer_or_letter) \ 00142 or is_.an_integer(integer_or_letter)): 00143 #___ 00144 raise error.UncompatibleType(integer_or_letter, \ 00145 "integer_or_letter") 00146 00147 if not (isinstance(objct, Exponented) or objct is None): 00148 raise error.UncompatibleType(objct, "Exponented|None") 00149 00150 self._name = integer_or_letter 00151 self._right_hand_side = objct.clone() 00152 00153 00154 00155 00156 00157 # -------------------------------------------------------------------------- 00158 ## 00159 # @brief Returns the right hand side of the Expression e.g. the Expression 00160 # in itself 00161 def get_right_hand_side(self): 00162 return self._right_hand_side 00163 00164 00165 00166 00167 00168 right_hand_side = property(get_right_hand_side, 00169 doc = "Right hand side of the object") 00170 00171 00172 00173 00174 00175 # -------------------------------------------------------------------------- 00176 ## 00177 # @brief Sets the right hand side of the Expression 00178 def set_right_hand_side(self, arg): 00179 if not (isinstance(objct, Exponented)): 00180 raise error.UncompatibleType(objct, "Exponented") 00181 00182 self._right_hand_side = arg.clone() 00183 00184 00185 00186 00187 00188 # -------------------------------------------------------------------------- 00189 ## 00190 # @brief Creates a string of the given object in the given ML 00191 # @param options Any options 00192 # @return The formated string 00193 def into_str(self, **options): 00194 global expression_begins 00195 # Expression objects displaying 00196 if is_.an_integer(self.name): 00197 i = self.name 00198 if i < len(alphabet.UPPERCASE): 00199 final_name = alphabet.UPPERCASE[i] 00200 else: 00201 nb_letters = len(alphabet.UPPERCASE) 00202 final_name = alphabet.UPPERCASE[i - 00203 nb_letters 00204 * 00205 int(i/nb_letters) 00206 ] \ 00207 + MARKUP['opening_subscript'] \ 00208 + str(int(i/nb_letters)) \ 00209 + MARKUP['closing_subscript'] 00210 00211 elif is_.a_string(self.name): 00212 final_name = self.name 00213 00214 expression_begins = True 00215 00216 return final_name \ 00217 + MARKUP['equal'] \ 00218 + self.right_hand_side.into_str(force_expression_begins=True, 00219 **options) 00220 00221 00222 00223 00224 00225 00226 # -------------------------------------------------------------------------- 00227 ## 00228 # @brief Create a string of the expression's exp. & red. in the given ML 00229 # @param options Any options 00230 # @return The formated string of the expression's resolution 00231 def auto_expansion_and_reduction(self, **options): 00232 global expression_begins 00233 aux_expr = self.right_hand_side 00234 result = "" 00235 00236 #DEBUG 00237 # print aux_expr.into_str() 00238 00239 00240 # Complete expansion & reduction of any expression :o) 00241 while aux_expr != None: 00242 result += MARKUP['opening_math_style2'] \ 00243 + Expression(self.name, 00244 aux_expr).into_str() \ 00245 + MARKUP['closing_math_style2'] \ 00246 + MARKUP['newline'] \ 00247 + "\n" 00248 #DEBUG 00249 #print "\n ++ " + str(aux_expr) + " ++ \n" 00250 #print "\n ++ " + str(type(aux_expr)) + " ++ \n" 00251 aux_expr = aux_expr.expand_and_reduce_next_step() 00252 00253 return result 00254 00255 00256 00257 00258 00259 # ------------------------------------------------------------------------------ 00260 # -------------------------------------------------------------------------- 00261 # ------------------------------------------------------------------------------ 00262 ## 00263 # @class Equality 00264 # @brief These are object of the kind : Exponented = Exponented [= ...] 00265 class Equality(ComposedCalculable): 00266 00267 00268 00269 00270 00271 # -------------------------------------------------------------------------- 00272 ## 00273 # @brief Constructor 00274 # @warning Might raise an UncompatibleType exception. 00275 # @param objcts is a [Exponented] of 2 elements at least 00276 # @option equal_signs Contains a list of equal/not equal signs. Must be as 00277 # @option long as len(objcts) - 1. The signs are "=" or "neq" 00278 # @return One instance of Equality 00279 def __init__(self, objcts, **options): 00280 # just check if the given arguments are right 00281 if not (isinstance(objcts, list)): 00282 raise error.UncompatibleType(objcts, \ 00283 "should be a LIST (of two Exponenteds at least)") 00284 00285 if not len(objcts) >= 2: 00286 raise error.UncompatibleType(objcts, \ 00287 "should be a list of TWO Exponenteds AT LEAST") 00288 00289 for i in range(len(objcts)): 00290 if not isinstance(objcts[i], Exponented): 00291 raise error.UncompatibleType(objcts[i], \ 00292 "should be an Exponented") 00293 00294 if 'equal_signs' in options: 00295 if not type(options['equal_signs']) == list: 00296 raise error.UncompatibleType(options['equal_signs'], \ 00297 "should be a list") 00298 if not len(options['equal_signs']) == len(objcts) - 1: 00299 raise error.UncompatibleType(options['equal_signs'], \ 00300 "should contain " \ 00301 + str(len(objcts) - 1) \ 00302 + " elements.") 00303 00304 for i in range(len(options['equal_signs'])): 00305 if not (options['equal_signs'][i] == '=' \ 00306 or options['equal_signs'][i] == 'neq'): 00307 #___ 00308 raise error.UncompatibleType(options['equal_signs'][i], \ 00309 " should be '=' or 'neq'") 00310 00311 00312 self._elements = [] 00313 self._equal_signs = [] 00314 00315 for i in range(len(objcts)): 00316 self._elements.append(objcts[i].clone()) 00317 00318 if 'equal_signs' in options: 00319 00320 if i < len(options['equal_signs']): 00321 sign_to_add = None 00322 00323 if options['equal_signs'][i] == '=': 00324 sign_to_add = 'equal' 00325 else: 00326 sign_to_add = 'not_equal' 00327 00328 self._equal_signs.append(sign_to_add) 00329 00330 else: 00331 self._equal_signs.append('equal') 00332 00333 00334 00335 00336 00337 # -------------------------------------------------------------------------- 00338 ## 00339 # @brief Returns the elements of the Equality 00340 def get_elements(self): 00341 return self._elements 00342 00343 00344 00345 00346 00347 # -------------------------------------------------------------------------- 00348 ## 00349 # @brief Returns the equal signs series of the Equality 00350 def get_equal_signs(self): 00351 return self._equal_signs 00352 00353 00354 00355 00356 00357 elements = property(get_elements, doc = "Elements of the object") 00358 00359 equal_signs = property(get_equal_signs, doc = "Equal signs of the object") 00360 00361 00362 00363 00364 00365 # -------------------------------------------------------------------------- 00366 ## 00367 # @brief Creates a string of the given object in the given ML 00368 # @param options Any options 00369 # @return The formated string 00370 def into_str(self, **options): 00371 global expression_begins 00372 # Equality objects displaying 00373 00374 expression_begins = True 00375 00376 result = '' 00377 00378 if 'force_expression_markers' in options\ 00379 and options['force_expression_markers'] == 'yes': 00380 #___ 00381 result += MARKUP['opening_math_style2'] 00382 00383 00384 result += self.elements[0].into_str(force_expression_begins=True, 00385 **options) 00386 00387 for i in range(len(self.elements) - 1): 00388 result += MARKUP[self.equal_signs[i]] \ 00389 + self.elements[i+1].into_str( \ 00390 force_expression_begins=True, 00391 **options) 00392 00393 if 'force_expression_markers' in options\ 00394 and options['force_expression_markers'] == 'yes': 00395 #___ 00396 result += MARKUP['closing_math_style2'] 00397 00398 return result 00399 00400 00401 00402 00403 00404 # -------------------------------------------------------------------------- 00405 ## 00406 # @brief It is possible to index an Equality 00407 def __getitem__(self, i): 00408 return self._elements[i] 00409 00410 00411 def __setitem__(self, i, data): 00412 if not isinstance(data, Exponented): 00413 raise error.UncompatibleType(data, "should be a Exponented") 00414 00415 self._elements[i] = data.clone() 00416 00417 00418 00419 00420 00421 # -------------------------------------------------------------------------- 00422 ## 00423 # @brief Returns the number of elements of the Equality 00424 def __len__(self): 00425 return len(self._elements) 00426 00427 00428 00429 00430 00431 # ------------------------------------------------------------------------------ 00432 # -------------------------------------------------------------------------- 00433 # ------------------------------------------------------------------------------ 00434 ## 00435 # @class Equation 00436 # @brief One degree one variable. Sum=Sum. Name, number, left/right hand side. 00437 class Equation(ComposedCalculable): 00438 00439 00440 00441 00442 00443 # -------------------------------------------------------------------------- 00444 ## 00445 # @brief Constructor 00446 # @warning Might raise an UncompatibleType exception. 00447 # @param arg Equation|(Exponented, Exponented)|(RANDOMLY, ...) 00448 # |SubstitutableEquality 00449 # @return One instance of Equation 00450 def __init__(self, arg, **options): 00451 self._name = default.EQUATION_NAME 00452 self._number = '' 00453 self._left_hand_side = None 00454 self._right_hand_side = None 00455 self._variable_letter = default.MONOMIAL_LETTER 00456 00457 # First determine name and number of the equation 00458 if 'name' in options and is_.a_string(options['name']): 00459 self._name = options['name'] 00460 00461 if 'number' in options and is_.an_integer(options['number']): 00462 self._number = options['number'] 00463 00464 # Then the letter 00465 if 'variable_letter_name' in options \ 00466 and is_.a_string(options['variable_letter_name']): 00467 #___ 00468 self._variable_letter = options['variable_letter_name'][0] 00469 00470 # Then determine its left & right hand sides 00471 00472 if type(arg) == SubstitutableEquality: 00473 if not len(arg) == 2: 00474 raise error.ImpossibleAction("turn into an Equation a " \ 00475 "SubstitutableEquality having not exactly 2 " \ 00476 "members") 00477 literals_list = purge_from_duplicates(gather_literals(arg[0]) \ 00478 + gather_literals(arg[1]) 00479 ) 00480 if not len(literals_list) == 1: 00481 raise error.ImpossibleAction("create an Equation from a " \ 00482 + "SubstitutableEquality containing more than ONE unknown " \ 00483 + "literal. This one contains " + str(len(literals_list)) \ 00484 + " literals which are " \ 00485 + str([elt.dbg_str() for elt in literals_list])) 00486 00487 if not isinstance(literals_list[0], Value) \ 00488 and not literals_list[0].is_literal(): 00489 #___ 00490 raise error.WrongArgument(literals_list[0].dbg_str(), 00491 "a literal Value") 00492 00493 self.__init__((arg[0], arg[1]), 00494 variable_letter_name=literals_list[0].get_first_letter(), 00495 **options 00496 ) 00497 00498 # Different cases of tuples 00499 elif type(arg) == tuple and len(arg) == 2: 00500 00501 # 1st CASE 00502 # Given objects 00503 if isinstance(arg[0], Exponented) \ 00504 and isinstance(arg[1], Exponented): 00505 #___ 00506 if isinstance(arg[0], Sum): 00507 # TO FIX ? 00508 # this could be secured by checking the given Sum 00509 # is not containing only one term which would be also 00510 # a Sum 00511 self._left_hand_side = arg[0] 00512 else: 00513 self._left_hand_side = Sum(arg[0]) 00514 00515 if isinstance(arg[1], Sum): 00516 # TO FIX ? 00517 # this could be secured by checking the given Sum 00518 # is not containing only one term which would be also 00519 # a Sum 00520 self._right_hand_side = arg[1] 00521 else: 00522 self._right_hand_side = Sum(arg[1]) 00523 00524 # 2d CASE 00525 # RANDOMLY ! 00526 elif arg[0] == RANDOMLY: 00527 if arg[1] == 'basic_addition': 00528 self._left_hand_side = Polynomial([ 00529 Monomial(('+', 00530 1, 00531 1)), 00532 Monomial((randomly.sign(), 00533 randomly.integer(1, 00534 MAX_VALUE), 00535 0)) 00536 ]) 00537 00538 self._right_hand_side = Sum(Item((randomly.sign(), 00539 randomly.integer(1, 00540 MAX_VALUE) 00541 )) 00542 ) 00543 00544 00545 self._left_hand_side.term[0].set_letter( 00546 self.variable_letter) 00547 00548 00549 elif arg[1] == 'basic_addition_r': 00550 self._right_hand_side = Polynomial([ 00551 Monomial(('+', 00552 1, 00553 1)), 00554 Monomial((randomly.sign(), 00555 randomly.integer(1, 00556 MAX_VALUE), 00557 0)) 00558 ]) 00559 00560 self._left_hand_side = Sum(Item((randomly.sign(), 00561 randomly.integer(1, 00562 MAX_VALUE) 00563 )) 00564 ) 00565 00566 00567 self._right_hand_side.term[0].set_letter( 00568 self.variable_letter) 00569 elif arg[1] == 'any_basic_addition': 00570 cst_list = list() 00571 m1 = Monomial((randomly.sign(plus_signs_ratio=0.8), 00572 1, 00573 1)) 00574 00575 m1.set_letter(self.variable_letter) 00576 00577 m2 = Monomial((randomly.sign(), 00578 randomly.integer(1,MAX_VALUE), 00579 0)) 00580 00581 m3 = Monomial((randomly.sign(), 00582 randomly.integer(1,MAX_VALUE), 00583 0)) 00584 00585 cst_list.append(m2) 00586 cst_list.append(m3) 00587 00588 drawn_to_be_with_x = randomly.pop(cst_list) 00589 00590 polyn_list = list() 00591 polyn_list.append(m1) 00592 polyn_list.append(drawn_to_be_with_x) 00593 00594 polyn = Polynomial([randomly.pop(polyn_list), 00595 randomly.pop(polyn_list)]) 00596 sides = list() 00597 sides.append(polyn) 00598 sides.append(Sum(randomly.pop(cst_list))) 00599 00600 self._left_hand_side = randomly.pop(sides) 00601 self._right_hand_side = randomly.pop(sides) 00602 00603 elif arg[1] == 'basic_multiplication': 00604 self._left_hand_side = Sum( 00605 Monomial(( \ 00606 randomly.sign(plus_signs_ratio=0.75), 00607 randomly.integer(2, MAX_VALUE), 00608 1)) 00609 ) 00610 00611 00612 self._right_hand_side = Sum( 00613 Item(( \ 00614 randomly.sign(plus_signs_ratio=0.75), 00615 randomly.integer(1, MAX_VALUE), 00616 1)) 00617 ) 00618 00619 00620 self._left_hand_side.term[0].\ 00621 set_letter(self.variable_letter) 00622 00623 elif arg[1] == 'basic_multiplication_r': 00624 self._right_hand_side = Sum( 00625 Monomial(( \ 00626 randomly.sign(plus_signs_ratio=0.75), 00627 randomly.integer(2, MAX_VALUE), 00628 1)) 00629 ) 00630 00631 00632 self._left_hand_side = Sum( 00633 Item(( \ 00634 randomly.sign(plus_signs_ratio=0.75), 00635 randomly.integer(1, MAX_VALUE), 00636 1)) 00637 ) 00638 00639 00640 self._right_hand_side.term[0].\ 00641 set_letter(self.variable_letter) 00642 00643 elif arg[1] == 'any_basic_multiplication': 00644 m1 = Monomial((randomly.sign(plus_signs_ratio=0.75), 00645 randomly.integer(2, MAX_VALUE), 00646 1)) 00647 m1.set_letter(self.variable_letter) 00648 00649 m2 = Item((randomly.sign(plus_signs_ratio=0.75), 00650 randomly.integer(1, MAX_VALUE), 00651 1)) 00652 00653 items_list = list() 00654 items_list.append(Sum(m1)) 00655 items_list.append(Sum(m2)) 00656 00657 self._left_hand_side = randomly.pop(items_list) 00658 self._right_hand_side = randomly.pop(items_list) 00659 00660 elif arg[1] == 'any_basic': # code duplication... done quickly 00661 if randomly.heads_or_tails(): 00662 m1 = Monomial((randomly.sign(plus_signs_ratio= \ 00663 0.75), 00664 randomly.integer(2, MAX_VALUE), 00665 1)) 00666 m1.set_letter(self.variable_letter) 00667 00668 m2 = Item((randomly.sign(plus_signs_ratio=0.75), 00669 randomly.integer(1, MAX_VALUE), 00670 1)) 00671 00672 items_list = list() 00673 items_list.append(Sum(m1)) 00674 items_list.append(Sum(m2)) 00675 00676 self._left_hand_side = randomly.pop(items_list) 00677 self._right_hand_side = randomly.pop(items_list) 00678 else: 00679 cst_list = list() 00680 m1 = Monomial((randomly.sign(plus_signs_ratio= \ 00681 0.8), 00682 1, 00683 1)) 00684 m1.set_letter(self.variable_letter) 00685 00686 m2 = Monomial((randomly.sign(), 00687 randomly.integer(1,MAX_VALUE), 00688 0)) 00689 m3 = Monomial((randomly.sign(), 00690 randomly.integer(1,MAX_VALUE), 00691 0)) 00692 00693 cst_list.append(m2) 00694 cst_list.append(m3) 00695 00696 drawn_to_be_with_x = randomly.pop(cst_list) 00697 00698 polyn_list = list() 00699 polyn_list.append(m1) 00700 polyn_list.append(drawn_to_be_with_x) 00701 00702 polyn = Polynomial([randomly.pop(polyn_list), 00703 randomly.pop(polyn_list)]) 00704 sides = list() 00705 sides.append(polyn) 00706 sides.append(Sum(randomly.pop(cst_list))) 00707 00708 self._left_hand_side = randomly.pop(sides) 00709 self._right_hand_side = randomly.pop(sides) 00710 00711 elif arg[1] == 'classic' \ 00712 or arg[1] == 'classic_r' \ 00713 or arg[1] == 'classic_x_twice' \ 00714 or arg[1] == 'any_classic': 00715 #___ 00716 # Let's build 00717 # classic : ax + b = d | b + ax = d 00718 # classic_r : d = ax + b | d = b + ax 00719 # classic_x_twice : ax + b = cx + d | ax + b = cx | 00720 # cx = ax + b | b + ax = cx + d etc. 00721 box = list() 00722 ax = Monomial((randomly.sign(plus_signs_ratio=0.65), 00723 randomly.integer(1,MAX_VALUE), 00724 1)) 00725 ax.set_letter(self.variable_letter) 00726 00727 b = Monomial((randomly.sign(), 00728 randomly.integer(1,MAX_VALUE), 00729 0)) 00730 cx = Monomial((randomly.sign(plus_signs_ratio=0.65), 00731 randomly.integer(1,MAX_VALUE), 00732 1)) 00733 cx.set_letter(self.variable_letter) 00734 00735 d = Monomial((randomly.sign(), 00736 randomly.integer(1,MAX_VALUE), 00737 0)) 00738 00739 box.append(ax) 00740 box.append(b) 00741 00742 polyn1 = Polynomial([randomly.pop(box), 00743 randomly.pop(box)]) 00744 00745 if arg[1] == 'classic' or arg[1] == 'classic_r': 00746 polyn2 = Polynomial([d]) 00747 elif arg[1] == 'classic_x_twice': 00748 if randomly.decimal_0_1() > 0.3: 00749 box = list() 00750 box.append(cx) 00751 box.append(d) 00752 polyn2 = Polynomial([randomly.pop(box), 00753 randomly.pop(box)]) 00754 else: 00755 polyn2 = Polynomial([cx]) 00756 00757 elif arg[1] == 'any_classic': 00758 box = list() 00759 random_nb = randomly.decimal_0_1() 00760 if random_nb < 0.4: 00761 box.append(cx) 00762 box.append(d) 00763 polyn2 = Polynomial([randomly.pop(box), 00764 randomly.pop(box)]) 00765 elif random_nb < 0.7: 00766 polyn2 = Polynomial([d]) 00767 00768 else: 00769 polyn2 = Polynomial([cx]) 00770 00771 if arg[1] == 'classic': 00772 self._left_hand_side = polyn1 00773 self._right_hand_side = polyn2 00774 elif arg[1] == 'classic_r': 00775 self._left_hand_side = polyn2 00776 self._right_hand_side = polyn1 00777 elif arg[1] == 'classic_x_twice' \ 00778 or arg[1] == 'any_classic': 00779 box = list() 00780 box.append(polyn1) 00781 box.append(polyn2) 00782 self._left_hand_side = randomly.pop(box) 00783 self._right_hand_side = randomly.pop(box) 00784 00785 elif arg[1] == 'classic_with_fractions': 00786 # the following code is copied from Calculation.py 00787 # -> must be factorized 00788 randomly_position = randomly.integer(0, 00789 16, 00790 weighted_table=\ 00791 FRACTIONS_SUMS_SCALE_TABLE) 00792 00793 chosen_seed_and_generator = FRACTIONS_SUMS_TABLE[\ 00794 randomly_position] 00795 00796 00797 seed = randomly.integer(2, chosen_seed_and_generator[1]) 00798 00799 # The following test is only intended to avoid having "high" 00800 # results too often. We just check if the common denominator 00801 # will be higher than 75 (arbitrary) and if yes, we 00802 # redetermine 00803 # it once. We don't do it twice since we don't want to 00804 # totally 00805 # forbid high denominators. 00806 if seed * chosen_seed_and_generator[0][0] \ 00807 * chosen_seed_and_generator[0][1] >= 75: 00808 #___ 00809 seed = randomly.integer(2, chosen_seed_and_generator[1]) 00810 00811 lil_box = [0, 1] 00812 gen1 = chosen_seed_and_generator[0][lil_box.pop()] 00813 gen2 = chosen_seed_and_generator[0][lil_box.pop()] 00814 00815 den1 = Item(gen1*seed) 00816 den2 = Item(gen2*seed) 00817 00818 temp1 = randomly.integer(1, 20) 00819 temp2 = randomly.integer(1, 20) 00820 00821 num1 = Item(temp1 // gcd(temp1, gen1*seed)) 00822 num2 = Item(temp2 // gcd(temp2, gen2*seed)) 00823 00824 f1 = Fraction((randomly.sign(plus_signs_ratio=0.7), 00825 num1, 00826 den1)) 00827 f2 = Fraction((randomly.sign(plus_signs_ratio=0.7), 00828 num2, 00829 den2)) 00830 00831 # END OF COPIED CODE --------------------------------------- 00832 00833 box = list() 00834 ax = Monomial((Fraction((randomly.sign(\ 00835 plus_signs_ratio=0.7), 00836 Item(randomly.integer(1, 00837 10)), 00838 Item(randomly.integer(2, 00839 10)) 00840 )).simplified(), 00841 1)) 00842 ax.set_letter(self.variable_letter) 00843 00844 b = Monomial((f1.simplified(), 0)) 00845 d = Monomial((f2.simplified(), 0)) 00846 00847 box.append(ax) 00848 box.append(b) 00849 00850 self._left_hand_side = Polynomial([randomly.pop(box), 00851 randomly.pop(box)]) 00852 00853 self._right_hand_side = Sum([d]) 00854 00855 00856 elif arg[1] == 'any_simple_expandable' \ 00857 or arg[1] == 'any_double_expandable': 00858 #___ 00859 # SIMPLE : 00860 # a monom0_polyn1 or a ±(...) on one side 00861 # + one Monomial with the monom0_polyn1 00862 # and one or two Monomials on the other side 00863 # DOUBLE : 00864 # The same plus another expandable. 00865 00866 # Creation of the expandables, to begin with... 00867 if randomly.decimal_0_1() <= 0.8: 00868 aux_expd_1 = Expandable((RANDOMLY, 00869 'monom0_polyn1'), 00870 max_coeff=9) 00871 expd_kind = 'monom0_polyn1' 00872 00873 else: 00874 sign = randomly.sign() 00875 aux_expd_1 = Expandable(( \ 00876 Monomial((sign, 00877 1, 00878 0)), 00879 Polynomial((RANDOMLY, 00880 9, 00881 1, 00882 2)) 00883 )) 00884 if sign == '+': 00885 expd_kind = '+(...)' 00886 else: 00887 expd_kind = 'monom0_polyn1' 00888 00889 # Now we fill the boxes to draw from 00890 box_1 = [] 00891 box_2 = [] 00892 00893 additional_Monomial = Monomial((RANDOMLY, 00894 9, 00895 1)) 00896 additional_Monomial2 = Monomial((RANDOMLY, 00897 9, 00898 1)) 00899 00900 if additional_Monomial.degree == 0: 00901 additional_Monomial2.set_degree(1) 00902 00903 box_1.append(aux_expd_1) 00904 00905 if expd_kind == '+(...)' or randomly.decimal_0_1() <= 0.5: 00906 box_1.append(additional_Monomial) 00907 00908 box_2.append(Monomial((RANDOMLY, 9, 1))) 00909 00910 if randomly.decimal_0_1() <= 0.25: 00911 #___ 00912 box_2.append(additional_Monomial2) 00913 00914 if arg[1] == 'any_double_expandable': 00915 if randomly.decimal_0_1() <= 0.8: 00916 aux_expd_2 = Expandable((RANDOMLY, 00917 'monom0_polyn1'), 00918 max_coeff=9) 00919 00920 else: 00921 sign = randomly.sign() 00922 aux_expd_2 = Expandable(( \ 00923 Monomial((sign, 00924 1, 00925 0)), 00926 Polynomial((RANDOMLY, 00927 9, 00928 1, 00929 2)) 00930 )) 00931 00932 if randomly.decimal_0_1() <= 0.5: 00933 box_1.append(aux_expd_2) 00934 else: 00935 box_2.append(aux_expd_2) 00936 00937 boxes = [box_1, box_2] 00938 box_left = randomly.pop(boxes) 00939 box_right = randomly.pop(boxes) 00940 00941 left_list = list() 00942 right_list = list() 00943 00944 for i in range(len(box_left)): 00945 left_list.append(randomly.pop(box_left)) 00946 00947 for i in range(len(box_right)): 00948 right_list.append(randomly.pop(box_right)) 00949 00950 self._left_hand_side = Sum(left_list) 00951 self._right_hand_side = Sum(right_list) 00952 00953 00954 00955 # All other unforeseen cases : an exception is raised. 00956 else: 00957 raise error.UncompatibleType(arg, 00958 "(Exponented, Exponented)|" \ 00959 + "(RANDOMLY, <option>)") 00960 00961 # Another Equation to copy 00962 elif isinstance(arg, Equation): 00963 self._name = arg.name 00964 self._number = arg.number 00965 self._left_hand_side = arg.left_hand_side.clone() 00966 self._right_hand_side = arg.right_hand_side.clone() 00967 self._variable_letter = arg.variable_letter 00968 00969 else: 00970 raise error.UncompatibleType(arg, "Equation|tuple") 00971 00972 00973 00974 00975 # -------------------------------------------------------------------------- 00976 ## 00977 # @brief Returns the number of the Equality 00978 def get_number(self): 00979 return self._number 00980 00981 00982 00983 00984 00985 # -------------------------------------------------------------------------- 00986 ## 00987 # @brief Getter for left hand side 00988 def get_left_hand_side(self): 00989 return self._left_hand_side 00990 00991 00992 00993 00994 00995 # -------------------------------------------------------------------------- 00996 ## 00997 # @brief Getter for right hand side 00998 def get_right_hand_side(self): 00999 return self._right_hand_side 01000 01001 01002 01003 01004 01005 # -------------------------------------------------------------------------- 01006 ## 01007 # @brief Getter for the variable letter 01008 def get_variable_letter(self): 01009 return self._variable_letter 01010 01011 01012 01013 01014 01015 number = property(get_number, doc = "Number of the Equation") 01016 01017 left_hand_side = property(get_left_hand_side, 01018 doc = "Left hand side of the Equation") 01019 01020 right_hand_side = property(get_right_hand_side, 01021 doc = "Right hand side of the Equation") 01022 01023 variable_letter = property(get_variable_letter, 01024 doc = "Variable letter of the Equation") 01025 01026 01027 01028 # -------------------------------------------------------------------------- 01029 ## 01030 # @brief Sets the number of the Equation 01031 def set_number(self, arg): 01032 if not type(arg) == int: 01033 raise error.WrongArgument(str(type(arg)), "int") 01034 01035 self._number = str(arg) 01036 01037 01038 01039 01040 01041 # -------------------------------------------------------------------------- 01042 ## 01043 # @brief Setter for hand sides 01044 # @warning Might raise an UncompatibleType exception. 01045 # @return Nothing, just sets the given argument to the left hand side, 01046 # turned into a Sum if necessary 01047 def set_hand_side(self, left_or_right, arg): 01048 01049 if not (left_or_right == "left" or left_or_right == "right"): 01050 raise error.UncompatibleType(left_or_right, 01051 '"left" or "right"') 01052 01053 if isinstance(arg, Exponented): 01054 if isinstance(arg, Sum): 01055 # TO FIX ? 01056 # this could be secured by checking the given Sum 01057 # is not containing only one term which would be also 01058 # a Sum 01059 if left_or_right == "left": 01060 self._left_hand_side = arg 01061 else: 01062 self._right_hand_side = arg 01063 else: 01064 if left_or_right == "left": 01065 self._left_hand_side = Sum(arg) 01066 else: 01067 self._right_hand_side = Sum(arg) 01068 01069 else: 01070 raise error.UncompatibleType(arg, "Equation|tuple") 01071 01072 01073 01074 01075 01076 # -------------------------------------------------------------------------- 01077 ## 01078 # @brief Creates a string of the given object in the given ML 01079 # @param options Any options 01080 # @return The formated string 01081 def into_str(self, **options): 01082 global expression_begins 01083 # Equation objects displaying 01084 beginning = '' 01085 01086 if 'display_name' in options: 01087 if self.number == '': 01088 beginning = MARKUP['opening_bracket'] \ 01089 + self.name \ 01090 + MARKUP['closing_bracket'] \ 01091 + MARKUP['colon'] \ 01092 + MARKUP['space'] 01093 else: 01094 beginning = MARKUP['opening_bracket'] \ 01095 + self.name \ 01096 + MARKUP['opening_subscript'] \ 01097 + str(self.number) \ 01098 + MARKUP['closing_subscript'] \ 01099 + MARKUP['closing_bracket'] \ 01100 + MARKUP['colon'] \ 01101 + MARKUP['space'] 01102 01103 left = self.left_hand_side.into_str(force_expression_begins=True) 01104 01105 right = self.right_hand_side.into_str(force_expression_begins=True) 01106 01107 egal_sign = MARKUP['equal'] 01108 01109 if self.left_hand_side.contains_a_rounded_number() \ 01110 or self.right_hand_side.contains_a_rounded_number(): 01111 #___ 01112 egal_sign = MARKUP['simeq'] 01113 01114 return beginning \ 01115 + left \ 01116 + egal_sign \ 01117 + right 01118 01119 01120 01121 01122 01123 # -------------------------------------------------------------------------- 01124 ## 01125 # @brief Raw display of the Equation (debugging method) 01126 # @return A string containing "type : sign coeff × X ^ degree" 01127 def dbg_str(self): 01128 return "\nEquation : " + str(self.name) \ 01129 + " " + str(self.number) \ 01130 + "\n Left hand side : " + self.left_hand_side.dbg_str() \ 01131 + "\n Right hand side : " + self.right_hand_side.dbg_str() \ 01132 + "\n Variable : " + str(self.variable_letter) 01133 01134 01135 01136 01137 01138 # -------------------------------------------------------------------------- 01139 ## 01140 # @brief Creates a string of the equation's resolution in the given ML 01141 # @param options Any options 01142 # @return The formated string of the equation's resolution 01143 def auto_resolution(self, **options): 01144 global expression_begins 01145 01146 # Complete resolution of the equation :o) 01147 result = "" 01148 01149 if not 'dont_display_equations_name' in options: 01150 if self.number == '': 01151 result = MARKUP['opening_math_style2'] \ 01152 + MARKUP['opening_bracket'] \ 01153 + self.name \ 01154 + MARKUP['closing_bracket'] \ 01155 + MARKUP['colon'] \ 01156 + MARKUP['space'] \ 01157 + MARKUP['closing_math_style2'] 01158 else: 01159 result = MARKUP['opening_math_style2'] \ 01160 + MARKUP['opening_bracket'] \ 01161 + self.name \ 01162 + MARKUP['opening_subscript'] \ 01163 + str(self.number) \ 01164 + MARKUP['closing_subscript'] \ 01165 + MARKUP['closing_bracket'] \ 01166 + MARKUP['colon'] \ 01167 + MARKUP['space'] \ 01168 + MARKUP['closing_math_style2'] 01169 01170 #result += MARKUP['newline'] 01171 01172 uline1 = "" 01173 uline2 = "" 01174 if 'underline_result' in options \ 01175 and options['underline_result'] in YES: 01176 #___ 01177 uline1 = MARKUP['open_underline'] 01178 uline2 = MARKUP['close_underline'] 01179 01180 eq_aux = None 01181 if isinstance(self, Equation) \ 01182 and not isinstance(self, CrossProductEquation): 01183 #___ 01184 eq_aux = Equation(self) 01185 elif isinstance(self, CrossProductEquation): 01186 eq_aux = CrossProductEquation(self) 01187 01188 eq_aux1 = None 01189 eq_aux2 = None 01190 equation_did_split_in_two = False 01191 go_on = True 01192 01193 while go_on: 01194 if not equation_did_split_in_two: 01195 01196 next_eq_aux = eq_aux.solve_next_step(**options) 01197 01198 if next_eq_aux is None and 'unit' in options: 01199 01200 result += MARKUP['opening_math_style1'] \ 01201 + uline1 \ 01202 + eq_aux.into_str() \ 01203 + MARKUP['open_text_in_maths'] \ 01204 + " " + str(options['unit']) \ 01205 + MARKUP['close_text_in_maths'] \ 01206 + uline2 \ 01207 + MARKUP['closing_math_style1'] 01208 else: 01209 result += MARKUP['opening_math_style1'] \ 01210 + eq_aux.into_str() \ 01211 + MARKUP['closing_math_style1'] 01212 01213 if next_eq_aux is None or type(next_eq_aux) == str \ 01214 or isinstance(next_eq_aux, tuple): 01215 #___ 01216 eq_aux = next_eq_aux 01217 else: 01218 eq_aux = Equation(next_eq_aux) 01219 01220 if isinstance(eq_aux, tuple): 01221 (eq_aux1, eq_aux2) = eq_aux 01222 equation_did_split_in_two = True 01223 01224 elif isinstance(eq_aux, str) or eq_aux is None: 01225 go_on = False 01226 01227 else: 01228 if isinstance(eq_aux1, Equation): 01229 next_eq_aux1 = eq_aux1.solve_next_step(**options) 01230 01231 if isinstance(eq_aux2, Equation): 01232 next_eq_aux2 = eq_aux2.solve_next_step(**options) 01233 01234 if eq_aux1 != None or eq_aux2 != None: 01235 result += MARKUP['opening_math_style1'] 01236 01237 01238 if isinstance(eq_aux1, Equation): 01239 if next_eq_aux1 is None: 01240 result += uline1 01241 01242 result += eq_aux1.into_str() 01243 01244 if next_eq_aux1 is None and 'unit' in options: 01245 result += MARKUP['open_text_in_maths'] \ 01246 + " " + str(options['unit']) \ 01247 + MARKUP['close_text_in_maths'] 01248 01249 if next_eq_aux1 is None: 01250 result += uline2 01251 01252 01253 if isinstance(eq_aux2, Equation): 01254 result += " " + _("or") + " " 01255 01256 elif eq_aux1 != None: 01257 result += eq_aux1 01258 01259 if isinstance(eq_aux2, Equation): 01260 01261 if next_eq_aux2 is None: 01262 result += uline1 01263 01264 result += eq_aux2.into_str() 01265 01266 if next_eq_aux2 is None and 'unit' in options: 01267 result += MARKUP['open_text_in_maths'] \ 01268 + " " + str(options['unit']) \ 01269 + MARKUP['close_text_in_maths'] 01270 01271 if next_eq_aux2 is None: 01272 result += uline2 01273 01274 elif eq_aux2 != None: 01275 result += eq_aux2 01276 eq_aux2 = None 01277 01278 if not isinstance(eq_aux1, Equation) \ 01279 and not isinstance(eq_aux2, Equation): 01280 #___ 01281 go_on = False 01282 01283 if eq_aux1 != None or eq_aux2 != None: 01284 result += MARKUP['closing_math_style1'] 01285 01286 if isinstance(eq_aux1, Equation): 01287 eq_aux1 = eq_aux1.solve_next_step(**options) 01288 01289 if isinstance(eq_aux2, Equation): 01290 eq_aux2 = eq_aux2.solve_next_step(**options) 01291 01292 01293 01294 if (not equation_did_split_in_two): 01295 if eq_aux is None: 01296 pass 01297 else: 01298 result += eq_aux + MARKUP['newline'] 01299 else: 01300 if eq_aux1 is None and eq_aux2 is None: 01301 pass 01302 else: 01303 if eq_aux1 is None: 01304 result += eq_aux2 + MARKUP['newline'] 01305 else: 01306 result += eq_aux1 + MARKUP['newline'] 01307 01308 01309 #if 'get_solution' in options and options['get_solution'] in YES: 01310 01311 01312 01313 return result 01314 01315 01316 01317 01318 01319 # -------------------------------------------------------------------------- 01320 ## 01321 # @brief Creates the next Equation object in the resolution 01322 # @todo Expandables (which have to get checked first, btw) ! 01323 # @todo check the case -x = -7 where the - belongs to the Item's value 01324 # @return An Equation 01325 def solve_next_step(self, **options): 01326 new_eq = Equation(self) 01327 #DEBUG 01328 debug.write("\nEntering [solve_next_step] " \ 01329 + "with Equation :\n" \ 01330 + new_eq.dbg_str() + "\n", 01331 case=debug.solve_next_step) 01332 01333 # CASE 0 : preparing the Equation : getting recursively rid of 01334 # imbricated Sums 01335 if isinstance(new_eq.left_hand_side.term[0], Sum) \ 01336 and len(new_eq.left_hand_side) == 1: 01337 #___ 01338 #DEBUG 01339 debug.write("\n[solve_next_step] CASE-0s-left\n", 01340 case=debug.solve_next_step) 01341 new_eq.set_hand_side("left", new_eq.left_hand_side.term[0]) 01342 return new_eq.solve_next_step(**options) 01343 01344 elif isinstance(new_eq.right_hand_side.term[0], Sum) \ 01345 and len(new_eq.right_hand_side) == 1: 01346 #___ 01347 #DEBUG 01348 debug.write("\n[solve_next_step] CASE-0s-right\n", 01349 case=debug.solve_next_step) 01350 new_eq.set_hand_side("right", new_eq.right_hand_side.term[0]) 01351 return new_eq.solve_next_step(**options) 01352 01353 if isinstance(new_eq.left_hand_side.term[0], Product) \ 01354 and len(new_eq.left_hand_side) == 1 \ 01355 and len(new_eq.left_hand_side.term[0]) == 1: 01356 #___ 01357 #DEBUG 01358 debug.write("\n[solve_next_step] CASE-0p-left\n", 01359 case=debug.solve_next_step) 01360 new_eq.set_hand_side("left", 01361 Sum(new_eq.left_hand_side.term[0].factor[0])) 01362 return new_eq.solve_next_step(**options) 01363 01364 elif isinstance(new_eq.right_hand_side.term[0], Product) \ 01365 and len(new_eq.right_hand_side) == 1 \ 01366 and len(new_eq.right_hand_side.term[0]) == 1: 01367 #___ 01368 #DEBUG 01369 debug.write("\n[solve_next_step] CASE-0p-right\n", 01370 case=debug.solve_next_step) 01371 new_eq.set_hand_side("right", 01372 Sum(new_eq.right_hand_side.term[0].factor[0])) 01373 return new_eq.solve_next_step(**options) 01374 01375 next_left_X = new_eq.left_hand_side.expand_and_reduce_next_step() 01376 next_right_X = new_eq.right_hand_side.expand_and_reduce_next_step() 01377 #next_left_C = new_eq.left_hand_side.calculate_next_step() 01378 #next_right_C = new_eq.right_hand_side.calculate_next_step() 01379 01380 if debug.ENABLED and debug.solve_next_step: 01381 if not (isinstance(next_left_X, Calculable) \ 01382 or isinstance(next_left_X, ComposedCalculable)): 01383 #___ 01384 next_left_X_str = str(next_left_X) 01385 else: 01386 next_left_X_str = next_left_X.dbg_str() 01387 debug.write("\n[solve_next_step] " \ 01388 + "'decimal_result' is in options : " \ 01389 + str('decimal_result' in options) \ 01390 + " len(new_eq.left_hand_side) == " \ 01391 + str(len(new_eq.left_hand_side)) \ 01392 + "\nnext_left_X is : " \ 01393 + next_left_X_str \ 01394 + "\nnew_eq.left_hand_side.term[0].is_literal() : " \ 01395 + str(new_eq.left_hand_side.term[0].is_literal()) \ 01396 + " len(new_eq.right_hand_side) == " \ 01397 + str(len(new_eq.right_hand_side)) \ 01398 + "\nisinstance(new_eq.right_hand_side.term[0], " \ 01399 + "Fraction)" \ 01400 + str(isinstance(new_eq.right_hand_side.term[0], 01401 Fraction)) \ 01402 + "\n", 01403 case=debug.solve_next_step) 01404 01405 if ('skip_fraction_simplification' in options \ 01406 and not 'decimal_result' in options)\ 01407 and len(new_eq.left_hand_side) == 1 \ 01408 and next_left_X is None \ 01409 and not new_eq.left_hand_side.term[0].is_numeric() \ 01410 and len(new_eq.right_hand_side) == 1 \ 01411 and isinstance(new_eq.right_hand_side.term[0], Fraction) \ 01412 and new_eq.right_hand_side.term[0].is_reducible(): 01413 #___ 01414 new_eq.set_hand_side("right", 01415 new_eq.\ 01416 right_hand_side.term[0].completely_reduced()) 01417 01418 elif ('skip_fraction_simplification' in options \ 01419 and 'decimal_result' in options)\ 01420 and len(new_eq.left_hand_side) == 1 \ 01421 and next_left_X is None \ 01422 and not new_eq.left_hand_side.term[0].is_numeric() \ 01423 and len(new_eq.right_hand_side) == 1 \ 01424 and isinstance(new_eq.right_hand_side.term[0], Fraction) \ 01425 and new_eq.right_hand_side.term[0].is_reducible(): 01426 #___ 01427 new_eq.set_hand_side("right", 01428 Item(new_eq.\ 01429 right_hand_side.term[0].evaluate(**options))) 01430 01431 01432 elif 'decimal_result' in options\ 01433 and len(new_eq.left_hand_side) == 1 \ 01434 and next_left_X is None \ 01435 and not new_eq.left_hand_side.term[0].is_numeric() \ 01436 and len(new_eq.right_hand_side) == 1 \ 01437 and (isinstance(new_eq.right_hand_side.term[0], Quotient) \ 01438 or isinstance(new_eq.right_hand_side.term[0], SquareRoot)): 01439 #___ 01440 #DEBUG 01441 debug.write("\n[solve_next_step] Decimal Result CASE\n", 01442 case=debug.solve_next_step) 01443 new_eq.set_hand_side("right", 01444 new_eq.\ 01445 right_hand_side.\ 01446 expand_and_reduce_next_step(**options)) 01447 01448 # 1st CASE 01449 # Expand & reduce each side of the Equation, whenever possible 01450 elif (next_left_X != None) or (next_right_X != None): 01451 #___ 01452 #DEBUG 01453 debug.write("\n[solve_next_step] 1st CASE\n", 01454 case=debug.solve_next_step) 01455 if next_left_X != None: 01456 #___ 01457 new_eq.set_hand_side("left", next_left_X) 01458 01459 if next_right_X != None: 01460 #___ 01461 new_eq.set_hand_side("right", next_right_X) 01462 01463 01464 # 2d CASE 01465 # Irreducible SUMS, like 3 + x = 5 or x - 2 = 3x + 7 01466 # It seems useless to test if one side is reducible, if it was, 01467 # then it would have been treated in the first case. 01468 # After that case is treated, every literal term will be moved 01469 # to the left, and every numeric term moved to the right. 01470 elif (len(new_eq.left_hand_side) >= 2) \ 01471 or (len(new_eq.right_hand_side) >= 2) \ 01472 or (len(new_eq.right_hand_side) == 1 \ 01473 and not new_eq.right_hand_side.term[0].is_numeric()): 01474 #___ 01475 #DEBUG 01476 debug.write("\n[solve_next_step] 2d CASE\n", 01477 case=debug.solve_next_step) 01478 # All the literal objects will be moved to the left, 01479 # all numeric will be moved to the right 01480 #DEBUG 01481 debug.write("\néquation en cours : " \ 01482 + self.dbg_str() + "\n", 01483 case=debug.solve_next_step) 01484 01485 left_collected_terms = new_eq.left_hand_side.get_numeric_terms() 01486 right_collected_terms = new_eq.right_hand_side.get_literal_terms() 01487 01488 #DEBUG 01489 if debug.ENABLED and debug.solve_next_step: 01490 debug.write("\nleft content : " \ 01491 + self.left_hand_side.dbg_str(), 01492 case=debug.solve_next_step) 01493 debug.write("\nright content : " \ 01494 + self.right_hand_side.dbg_str(), 01495 case=debug.solve_next_step) 01496 01497 lstring = "\nleft collected terms : " 01498 01499 for t in left_collected_terms: 01500 lstring += t.dbg_str() 01501 01502 debug.write(lstring, 01503 case=debug.solve_next_step) 01504 01505 rstring = "\nright collected terms : " 01506 01507 for t in right_collected_terms: 01508 rstring += t.dbg_str() 01509 01510 debug.write(rstring + "\n", 01511 case=debug.solve_next_step) 01512 01513 # Special case of equations like 5 = x - 9 01514 # which should become x = 5 + 9 at the next line, instead of 01515 # -x = -5 - 9 (not suited for Pythagorean Equations) 01516 if new_eq.left_hand_side.is_numeric() \ 01517 and not new_eq.right_hand_side.is_numeric() \ 01518 and len(new_eq.right_hand_side) == 2 \ 01519 and len(right_collected_terms) == 1: 01520 #___ 01521 # DEBUG 01522 debug.write("\nEntered in the Special Case [part of 2d Case]", 01523 case=debug.solve_next_step) 01524 debug.write("\nisinstance(right_collected_terms[0], " \ 01525 + "Product) is " \ 01526 + str(isinstance(right_collected_terms[0], \ 01527 Product)) \ 01528 + "\nlen(right_collected_terms[0]) == 2 is " \ 01529 + str(len(right_collected_terms[0]) == 2) \ 01530 + "\nright_collected_terms[0][0] is " \ 01531 #+ str(right_collected_terms[0][0]) \ 01532 + "\nlen(right_collected_terms[0]) == 1 is " \ 01533 + str(len(right_collected_terms[0]) == 1) \ 01534 + "\nisinstance(right_collected_terms[0], " \ 01535 + "Item) is " \ 01536 + str(isinstance(right_collected_terms[0], \ 01537 Item)), 01538 case=debug.solve_next_step) 01539 01540 if (isinstance(right_collected_terms[0], Product) \ 01541 and len(right_collected_terms[0]) == 2 \ 01542 and right_collected_terms[0][0].is_positive()) \ 01543 or (isinstance(right_collected_terms[0], Product) \ 01544 and len(right_collected_terms[0]) == 1) \ 01545 or (isinstance(right_collected_terms[0], Item) \ 01546 and right_collected_terms[0].is_positive()): 01547 #___ 01548 # DEBUG 01549 debug.write("\nSpecial Case [part 2]", 01550 case=debug.solve_next_step) 01551 01552 return Equation((new_eq.right_hand_side, 01553 new_eq.left_hand_side)).solve_next_step() 01554 01555 01556 # Special Case 2 : 01557 # of Equations like 9 = 3x which should become x = 9/3 01558 # and not -3x = -9 01559 if new_eq.left_hand_side.is_numeric() \ 01560 and not new_eq.right_hand_side.is_numeric() \ 01561 and len(new_eq.right_hand_side) == 1 \ 01562 and isinstance(right_collected_terms[0], Product): 01563 #___ 01564 # DEBUG 01565 debug.write("\nSpecial Case [part 3]", 01566 case=debug.solve_next_step) 01567 01568 return Equation((new_eq.right_hand_side, 01569 new_eq.left_hand_side)).solve_next_step() 01570 01571 01572 #DEBUG 01573 #debug.write("\nleft_collected_terms : ", 01574 # case=debug.solve_next_step) 01575 01576 for term in left_collected_terms: 01577 #DEBUG 01578 #debug.write("\n" + str(term) + "\n", 01579 # case=debug.solve_next_step) 01580 new_eq.left_hand_side.remove(term) 01581 term.set_sign(sign_of_product(['-', term.sign])) 01582 new_eq.set_hand_side("right", 01583 Sum([new_eq.right_hand_side, term]) 01584 ) 01585 #DEBUG 01586 debug.write("\nNow, right_hand_side looks like : " \ 01587 + new_eq.right_hand_side.dbg_str() + "\n", 01588 case=debug.solve_next_step) 01589 01590 #DEBUG 01591 #debug.write("\nright_collected_terms : \n", 01592 # case=debug.solve_next_step) 01593 01594 for term in right_collected_terms: 01595 #DEBUG 01596 #debug.write("\n" + str(term) + "\n", 01597 # case=debug.solve_next_step) 01598 new_eq.right_hand_side.remove(term) 01599 #term.set_sign(sign_of_product(['-', term.sign])) 01600 term = Product([term, Item(-1)]).reduce_() 01601 new_eq.set_hand_side("left", Sum([new_eq.left_hand_side, 01602 term]) 01603 ) 01604 01605 new_eq.left_hand_side.reduce_() 01606 new_eq.right_hand_side.reduce_() 01607 #DEBUG 01608 #debug.write("\nafter reduction, "\ 01609 # + "right_hand_side looks like : " \ 01610 # + str(new_eq.right_hand_side) + "\n", 01611 # case=debug.solve_next_step) 01612 01613 01614 01615 # 3rd CASE 01616 # Weird cases like 0 = 1 or 2 = 2 01617 elif (new_eq.left_hand_side.term[0].is_numeric() \ 01618 and (new_eq.right_hand_side.term[0].is_numeric()) 01619 ): 01620 #___ 01621 #DEBUG 01622 debug.write("\n[solve_next_step] 3rd CASE\n", 01623 case=debug.solve_next_step) 01624 if new_eq.left_hand_side.term[0].raw_value == \ 01625 new_eq.right_hand_side.term[0].raw_value \ 01626 and new_eq.left_hand_side.get_sign() == \ 01627 new_eq.right_hand_side.get_sign(): 01628 #___ 01629 return _( \ 01630 "Any value of %(variable_name)s is solution of the equation.") \ 01631 % {'variable_name':new_eq.variable_letter} 01632 01633 else: 01634 return _("This equation has no solution.") 01635 01636 01637 01638 01639 # 4th CASE 01640 # Irreducible PRODUCTS ax = b or -x = b or x = b, 01641 # where a and b are Exponenteds. 01642 # The Product in the left side can't be numeric, or it would 01643 # have been already treated before 01644 elif isinstance(new_eq.left_hand_side.term[0], Product): 01645 #___ 01646 #DEBUG 01647 debug.write("\n[solve_next_step] 4th CASE\n", 01648 case=debug.solve_next_step) 01649 01650 # Let's replace the possibly remaining Monomial of degree 0 01651 # at the right by an equivalent Item or Fraction 01652 if isinstance(new_eq.right_hand_side.term[0], Monomial): 01653 if isinstance(new_eq.right_hand_side.term[0].factor[0], 01654 Item): 01655 #___ 01656 new_eq.right_hand_side.set_term(0, 01657 Item(new_eq.right_hand_side.term[0]) 01658 ) 01659 elif isinstance(new_eq.right_hand_side.term[0].factor[0], 01660 Fraction): 01661 #___ 01662 new_eq.right_hand_side.set_term(0, 01663 Fraction(new_eq.right_hand_side.term[0]) 01664 ) 01665 01666 01667 01668 # Let's get the numeric Exponented to remove from the left : 01669 coefficient = new_eq.left_hand_side.term[0].factor[0] 01670 01671 if coefficient.is_displ_as_a_single_1(): 01672 new_eq.set_hand_side("left", 01673 Item(new_eq.left_hand_side.term[0]\ 01674 .factor[1])) 01675 return new_eq.solve_next_step(**options) 01676 01677 elif coefficient.is_displ_as_a_single_minus_1(): 01678 new_eq.left_hand_side.term[0].set_opposite_sign() 01679 new_eq.right_hand_side.term[0].set_opposite_sign() 01680 01681 elif isinstance(coefficient, Item)\ 01682 and isinstance(new_eq.right_hand_side.term[0], Item): 01683 #___ 01684 new_eq.left_hand_side.term[0].set_factor(0, Item(1)) 01685 new_eq.set_hand_side("right", 01686 Fraction(('+', 01687 new_eq.right_hand_side.term[0], 01688 Item(coefficient) 01689 )) 01690 ) 01691 new_eq.right_hand_side.term[0].set_down_numerator_s_minus_sign() 01692 01693 else: 01694 new_eq.left_hand_side.term[0].set_factor(0, Item(1)) 01695 new_eq.right_hand_side.set_term(0, 01696 Quotient(('+', 01697 new_eq.right_hand_side.term[0], 01698 coefficient 01699 ), 01700 use_divide_symbol = 'yes') 01701 ) 01702 01703 # 5th CASE 01704 # Literal Items -x = b (or -x² = b) or x = b, or x² = b 01705 # where a and b are (reduced/simplified) Exponenteds. 01706 # The Item at the left can't be numeric, the case should have 01707 # been treated before 01708 elif isinstance(new_eq.left_hand_side.term[0], Item) \ 01709 and new_eq.left_hand_side.term[0].is_literal(): 01710 #___ 01711 if new_eq.left_hand_side.term[0].get_sign() == '-': 01712 new_eq.left_hand_side.term[0].set_opposite_sign() 01713 new_eq.right_hand_side.term[0].set_opposite_sign() 01714 else: 01715 # CASES x² = b 01716 if new_eq.left_hand_side.term[0].exponent == Value(2): 01717 01718 if new_eq.right_hand_side.term[0].is_negative(): 01719 return _("This equation has no solution.") 01720 01721 elif new_eq.left_hand_side.is_displ_as_a_single_0(): 01722 new_eq.left_hand_side.term[0].set_exponent(Value(1)) 01723 01724 else: 01725 temp_sqrt1 = SquareRoot(new_eq.\ 01726 right_hand_side.term[0]) 01727 temp_sqrt2 = SquareRoot(temp_sqrt1) 01728 temp_sqrt2.set_sign('-') 01729 temp_item = Item(new_eq.left_hand_side.term[0]) 01730 temp_item.set_exponent(Value(1)) 01731 new_eq1 = Equation((temp_item, 01732 temp_sqrt1)) 01733 new_eq2 = Equation((temp_item, 01734 temp_sqrt2)) 01735 01736 if 'pythagorean_mode' in options \ 01737 and options['pythagorean_mode'] == 'yes': 01738 #___ 01739 new_eq2 = MARKUP['open_text_in_maths'] \ 01740 + " " + _("because") + " " \ 01741 + temp_item.into_str( 01742 force_expression_begins=True) \ 01743 + " " + _("is positive.") \ 01744 + MARKUP['close_text_in_maths'] 01745 01746 return (new_eq1, new_eq2) 01747 01748 01749 # Now the exponent must be equivalent to a single 1 01750 # or the algorithm just doesn't know how to solve further. 01751 else: 01752 return None 01753 01754 01755 #DEBUG 01756 #debug.write("\nBefore having thrown away the neutrals : " \ 01757 # + "\n" \ 01758 # + str(new_eq) + "\n", 01759 # case=debug.solve_next_step) 01760 01761 # throwing away the possibly zeros left.. 01762 new_eq.set_hand_side("left", 01763 new_eq.left_hand_side.throw_away_the_neutrals() 01764 ) 01765 new_eq.set_hand_side("right", 01766 new_eq.right_hand_side.throw_away_the_neutrals() 01767 ) 01768 01769 #DEBUG 01770 #debug.write("\nafter having thrown away the neutrals," 01771 # + " right_hand_side looks like : " \ 01772 # + str(new_eq.right_hand_side) + "\n", 01773 # case=debug.solve_next_step) 01774 01775 #DEBUG 01776 debug.write("\nLeaving [solve_next_step] " \ 01777 + "with Equation :\n" \ 01778 + new_eq.dbg_str() + "\n", 01779 case=debug.solve_next_step) 01780 return new_eq 01781 01782 01783 01784 01785 01786 # ------------------------------------------------------------------------------ 01787 # -------------------------------------------------------------------------- 01788 # ------------------------------------------------------------------------------ 01789 ## 01790 # @class SubstitutableEquality 01791 # @brief Like an Equality with literals and the numeric values to replace them 01792 class SubstitutableEquality(Equality): 01793 01794 01795 01796 01797 01798 # -------------------------------------------------------------------------- 01799 ## 01800 # @brief Constructor 01801 # @warning Might raise an UncompatibleType exception. 01802 # @param objcts is a [Exponented] of 2 elements at least 01803 # @param subst_dict is a {literal Value: numeric Value} 01804 # @option equal_signs Contains a list of equal/not equal signs. Must be as 01805 # @option long as len(objcts) - 1. The signs are "=" or "neq" 01806 # @return One instance of SubstitutableEquality 01807 def __init__(self, objcts, subst_dict, **options): 01808 # This will check the objcts argument and take the possibly options 01809 # into account 01810 Equality.__init__(self, objcts, **options) 01811 01812 # Now, let's make the checks specific to SubstitutableEquality 01813 if not (isinstance(subst_dict, dict)): 01814 raise error.UncompatibleType(subst_dict, \ 01815 "should be a dictionnary") 01816 01817 if not check_lexicon_for_substitution(objcts, 01818 subst_dict, 01819 'at_least_one'): 01820 #___ 01821 raise error.WrongArgument(subst_dict, 01822 " a lexicon that matches the literals "\ 01823 + "of the objects list" 01824 ) 01825 01826 self._subst_dict = subst_dict 01827 01828 01829 01830 01831 01832 # -------------------------------------------------------------------------- 01833 ## 01834 # @brief Getter for the substitution dictionnary 01835 def get_subst_dict(self): 01836 return self._subst_dict 01837 01838 01839 01840 01841 01842 subst_dict = property(get_subst_dict, 01843 doc = "Substitution dictionnary of the SubstitutableEquality") 01844 01845 01846 01847 01848 01849 # -------------------------------------------------------------------------- 01850 ## 01851 # @brief Executes the substitution of the literal Values by the numeric 01852 def substitute(self): 01853 for elt in self._elements: 01854 elt.substitute(self.subst_dict) 01855 01856 return self 01857 01858 01859 01860 01861 01862 # ------------------------------------------------------------------------------ 01863 # -------------------------------------------------------------------------- 01864 # ------------------------------------------------------------------------------ 01865 ## 01866 # @class CrossProductEquation 01867 # @brief All objects that are displayable as Cross Product Equations 01868 class CrossProductEquation(Equation): 01869 01870 01871 01872 01873 01874 # -------------------------------------------------------------------------- 01875 ## 01876 # @brief Constructor 01877 # @param arg CrossProductEquation|(Quotient, Quotient)| 01878 # (num1, num2, deno1, deno2) 01879 # @param arg numx and denox are expected as Calculables 01880 def __init__(self, arg): 01881 if not (type(arg) == tuple or isinstance(arg, CrossProductEquation)): 01882 raise error.WrongArgument(str(type(arg)), 01883 "a tuple|CrossProductEquation") 01884 elif type(arg) == tuple and not (len(arg) == 2 or len(arg) == 4): 01885 raise error.WrongArgument("a tuple of length " + str(len(arg)), 01886 "a tuple of length 2 or 4") 01887 01888 if isinstance(arg, CrossProductEquation): 01889 self._name = arg.name 01890 self._number = arg.number 01891 self._left_hand_side = arg.left_hand_side.clone() 01892 self._right_hand_side = arg.right_hand_side.clone() 01893 self._variable_letter = arg.variable_letter 01894 self._variable_position = arg.variable_position 01895 self._variable_obj = arg.variable_obj.clone() 01896 01897 else: 01898 self._name = default.EQUATION_NAME 01899 self._number = '' 01900 01901 if len(arg) == 2: 01902 if not(isinstance(arg[0], Quotient) \ 01903 and isinstance(arg[1], Quotient)): 01904 #___ 01905 raise error.WrongArgument("a tuple of " + str(type(arg[0]))\ 01906 + "and of " + str(type(arg[1]), 01907 "a tuple of two Quotients") 01908 ) 01909 else: 01910 self._left_hand_side = arg[0].clone() 01911 self._right_hand_side = arg[1].clone() 01912 01913 elif len(arg) == 4: 01914 if not(isinstance(arg[0], Calculable) \ 01915 and isinstance(arg[1], Calculable) \ 01916 and isinstance(arg[2], Calculable) \ 01917 and isinstance(arg[3], Calculable)): 01918 #___ 01919 raise error.WrongArgument("a tuple of " + str(type(arg[0]))\ 01920 + ", " + str(type(arg[1])) \ 01921 + ", " + str(type(arg[2])) \ 01922 + "and " + str(type(arg[3])), 01923 "a tuple of four Calculables") 01924 else: 01925 self._left_hand_side = Quotient(('+', arg[0], arg[2])) 01926 self._right_hand_side = Quotient(('+', arg[1], arg[3])) 01927 01928 01929 # Let's find the variable 01930 # In the same time, we'll determine its position and the var obj 01931 stop = 0 01932 literal_position = 0 01933 literals = 0 01934 variable_letter = "" 01935 variable_obj = None 01936 # Don't change the order of elt below, it is inspired by this... 01937 # 0: x a 1: a x 2: a b 3: a b 01938 # b c b c c x x c 01939 for elt in [self.left_hand_side.numerator, 01940 self.right_hand_side.numerator, 01941 self.right_hand_side.denominator, 01942 self.left_hand_side.denominator]: 01943 if elt.is_literal(): 01944 literals += 1 01945 variable_letter += elt.into_str() 01946 variable_obj = elt.clone() 01947 stop = 1 01948 if not stop: 01949 literal_position += 1 01950 01951 if not literals == 1: 01952 raise error.WrongArgument("found " + str(literals) +\ 01953 "literal objects", 01954 "exactly one literal object "\ 01955 + "among 4") 01956 01957 self._variable_letter = variable_letter 01958 01959 self._variable_position = literal_position 01960 01961 self._variable_obj = variable_obj 01962 01963 01964 01965 01966 01967 # -------------------------------------------------------------------------- 01968 ## 01969 # @brief Getter for the variable obj 01970 def get_variable_obj(self): 01971 return self._variable_obj 01972 01973 01974 01975 01976 01977 # -------------------------------------------------------------------------- 01978 ## 01979 # @brief Getter for the variable position 01980 def get_variable_position(self): 01981 return self._variable_position 01982 01983 01984 01985 01986 01987 variable_obj = property(get_variable_obj, 01988 doc = "Variable object of the Equation") 01989 variable_position = property(get_variable_position, 01990 doc = "Variable position in the Equation") 01991 01992 01993 01994 01995 01996 # -------------------------------------------------------------------------- 01997 ## 01998 # @brief Creates the next Equation object in the resolution 01999 # @return An Equation 02000 def solve_next_step(self, **options): 02001 temp_table = Table([[self.left_hand_side.numerator, 02002 self.right_hand_side.numerator], 02003 [self.left_hand_side.denominator, 02004 self.right_hand_side.denominator] 02005 ] 02006 ) 02007 02008 new_eq = Equation((self.variable_obj, 02009 temp_table.cross_product((0, 1), 02010 self.variable_position) 02011 )) 02012 02013 return new_eq 02014 02015 02016 02017 02018 02019 02020 02021 02022 02023 # ------------------------------------------------------------------------------ 02024 # -------------------------------------------------------------------------- 02025 # ------------------------------------------------------------------------------ 02026 ## 02027 # @class Table 02028 # @brief All objects that are displayable as Tables 02029 class Table(Printable): 02030 02031 02032 02033 02034 02035 # -------------------------------------------------------------------------- 02036 ## 02037 # @brief Constructor 02038 # @param arg [[Calculable], [Calculable]] (the Calculables' lists must 02039 # have the same length) 02040 def __init__(self, arg): 02041 if not type(arg) == list: 02042 raise error.WrongArgument(arg, "a list (of two lists)") 02043 02044 if not len(arg) == 2: 02045 raise error.WrongArgument("a list of " + str(len(arg)) + "elements", 02046 "a list of 2 elements") 02047 02048 if not type(arg[0]) == list: 02049 raise error.WrongArgument(str(type(arg[0])), 02050 "arg[0] should be a list") 02051 02052 if not type(arg[1]) == list: 02053 raise error.WrongArgument(str(type(arg[1])), 02054 "arg[1] should be a list") 02055 02056 if not len(arg[0]) == len(arg[1]): 02057 raise error.WrongArgument("two lists of different lengths : " \ 02058 + str(len(arg[0])) + " and " \ 02059 + str(len(arg[1])), 02060 "two lists of the same length") 02061 02062 for j in range(2): 02063 for i in range(len(arg[j])): 02064 if not isinstance(arg[j][i], Calculable): 02065 raise error.WrongArgument("arg[" + str(j) + "][" \ 02066 + str(i) + "] is no instance of" \ 02067 + " Calculable ; type : " \ 02068 + str(type(arg[j][i])), 02069 "a Calculable") 02070 02071 02072 self._nb_of_cols = len(arg[0]) 02073 02074 self._data = arg 02075 02076 02077 02078 02079 02080 # -------------------------------------------------------------------------- 02081 ## 02082 # @brief Returns the Table's content as a list of two lists so it 02083 # can be addressed 02084 def get_cell(self): 02085 return self._data 02086 # -------------------------------------------------------------------------- 02087 cell = property(get_cell, 02088 doc = "t.cell is the complete Table t.cell[i][j] is a cell") 02089 02090 02091 02092 02093 02094 # -------------------------------------------------------------------------- 02095 ## 02096 # @brief Creates a string of the given object in the given ML 02097 # @param options Any options 02098 # @return The formated string 02099 # @todo Separate this from the LaTeX format (seems difficult to do) 02100 def into_str(self, **options): 02101 result = "" 02102 02103 if 'as_a_quotients_equality' in options \ 02104 and options['as_a_quotients_equality'] in YES: 02105 #___ 02106 for i in range(len(self)): 02107 result += Quotient(('+', 02108 self.cell[0][i], 02109 self.cell[1][i] 02110 ))\ 02111 .into_str(force_expression_begins=True) 02112 02113 if i < len(self) - 1: 02114 result += MARKUP['equal'] 02115 02116 else: # there, the table will be displayed normally, as a table 02117 content = [] 02118 02119 for i in range(2): 02120 for j in range(len(self)): 02121 content += [self.cell[i][j]\ 02122 .into_str(force_expression_begins=True)] 02123 02124 result = translator\ 02125 .create_table((2, len(self)), 02126 content, 02127 col_fmt=['c' for i in range(len(self))], 02128 borders='all' 02129 ) 02130 02131 02132 return result 02133 02134 02135 02136 02137 # -------------------------------------------------------------------------- 02138 ## 02139 # @brief Returns the number of columns of the Table 02140 def __len__(self): 02141 return self._nb_of_cols 02142 02143 02144 02145 02146 02147 # -------------------------------------------------------------------------- 02148 ## 02149 # @brief Produces the cross product of a cell among 4 given 02150 # @param cols : (nb of col 1, nb of col 2) 02151 # @param x_position : position of the unknown variable to compute 02152 # it will be 0, 1, 2 or 3 02153 # 0: x a 1: a x 2: a b 3: a b 02154 # b c b c c x x c 02155 # @param options Any options 02156 # @return A Quotient or possibly a Fraction 02157 def cross_product(self, col, x_position, **options): 02158 if col[0] >= len(self) or col[1] >= len(self): 02159 raise error.OutOfRangeArgument(str(col[0]) + " or " + str(col[1]), 02160 "should be < len(self) = " \ 02161 + str(len(self)) 02162 ) 02163 if not x_position in [0, 1, 2, 3]: 02164 raise error.OutOfRangeArgument(str(x_position), 02165 "should be in [0, 1, 2, 3]" 02166 ) 02167 02168 num = None 02169 02170 if x_position == 0 or x_position == 2: 02171 num = Product([self.cell[0][col[1]], 02172 self.cell[1][col[0]] 02173 ]) 02174 02175 elif x_position == 1 or x_position == 3: 02176 num = Product([self.cell[0][col[0]], 02177 self.cell[1][col[1]] 02178 ]) 02179 02180 deno = None 02181 02182 if x_position == 0: 02183 deno = self.cell[1][col[1]] 02184 02185 if self.cell[0][col[1]].is_displ_as_a_single_int() \ 02186 and self.cell[1][col[0]].is_displ_as_a_single_int() \ 02187 and self.cell[1][col[1]].is_displ_as_a_single_int(): 02188 #___ 02189 return Fraction((num, deno)) 02190 else: 02191 return Quotient(('+', num, deno)) 02192 02193 elif x_position == 1: 02194 deno = self.cell[1][col[0]] 02195 02196 if self.cell[0][col[0]].is_displ_as_a_single_int() \ 02197 and self.cell[1][col[1]].is_displ_as_a_single_int() \ 02198 and self.cell[1][col[0]].is_displ_as_a_single_int(): 02199 #___ 02200 return Fraction((num, deno)) 02201 else: 02202 return Quotient(('+', num, deno)) 02203 02204 elif x_position == 2: 02205 deno = self.cell[0][col[0]] 02206 02207 if self.cell[0][col[1]].is_displ_as_a_single_int() \ 02208 and self.cell[1][col[0]].is_displ_as_a_single_int() \ 02209 and self.cell[0][col[0]].is_displ_as_a_single_int(): 02210 #___ 02211 return Fraction((num, deno)) 02212 else: 02213 return Quotient(('+', num, deno)) 02214 02215 02216 elif x_position == 3: 02217 deno = self.cell[0][col[1]] 02218 02219 if self.cell[0][col[0]].is_displ_as_a_single_int() \ 02220 and self.cell[1][col[1]].is_displ_as_a_single_int() \ 02221 and self.cell[0][col[1]].is_displ_as_a_single_int(): 02222 #___ 02223 return Fraction((num, deno)) 02224 else: 02225 return Quotient(('+', num, deno)) 02226 02227 02228 02229 02230 02231 # -------------------------------------------------------------------------- 02232 ## 02233 # @brief Returns True if the Table is entirely numeric 02234 def is_numeric(self): 02235 for i in range(2): 02236 for j in range(len(self)): 02237 if not self.cell[i][j].is_numeric(): 02238 return False 02239 02240 return True 02241 02242 02243 02244 02245 02246 # ------------------------------------------------------------------------------ 02247 # -------------------------------------------------------------------------- 02248 # ------------------------------------------------------------------------------ 02249 ## 02250 # @class Table_UP 02251 # @brief All objects that are displayable as proportional Tables but uncomplete 02252 class Table_UP(Table): 02253 02254 02255 02256 02257 02258 # -------------------------------------------------------------------------- 02259 ## 02260 # @brief Constructor 02261 # @param coeff nb|numericCalculable 02262 # @param first_line [nb|numericCalculable] 02263 # @param info [None|(None|literalCalculable, 02264 # None|literalCalculable)] 02265 # info and first_line should have the same length 02266 # info should contain at least one None|(None, None) element 02267 # (means the column is completely numeric) 02268 def __init__(self, coeff, first_line, info): 02269 02270 if not is_.a_number(coeff) \ 02271 and not (isinstance(coeff, Calculable) and coeff.is_numeric()): 02272 #___ 02273 raise error.WrongArgument(str(type(coeff)), 02274 " a number or a numeric Calculable ") 02275 02276 if not type(first_line) == list: 02277 raise error.WrongArgument(str(type(first_line)), 02278 " a list ") 02279 02280 if not type(info) == list: 02281 raise error.WrongArgument(str(type(info)), 02282 " a list ") 02283 02284 if not len(info) == len(first_line): 02285 raise error.WrongArgument("two lists of lengths " + str(len(info))\ 02286 + " and " + str(len(first_line)), 02287 " two lists of the same length.") 02288 02289 for elt in first_line: 02290 if elt != None and not is_.a_number(elt) \ 02291 and not (isinstance(elt, Calculable) and elt.is_numeric()): 02292 #___ 02293 raise error.WrongArgument(str(type(elt)) + " " + elt.dbg_str(), 02294 "None | nb | numericCalculable ") 02295 02296 complete_cols = [] 02297 literals_positions = {} 02298 col_nb = 0 02299 02300 for i in range(len(first_line)): 02301 if first_line[i] is None and (info[i] is None \ 02302 or info[i] == (None, None)): 02303 #___ 02304 raise error.WrongArgument("first_line[i] and info[i] are" \ 02305 + " both equal to None", 02306 "only one of them can be None" \ 02307 + " in the same time") 02308 elt = info[i] 02309 02310 if not (elt is None or type(elt) == tuple): 02311 raise error.WrongArgument(str(type(elt)), 02312 " either None or a tuple ") 02313 02314 if elt is None or elt == (None, None): 02315 complete_cols += [col_nb] 02316 02317 if type(elt) == tuple: 02318 if not len(elt) == 2: 02319 raise error.WrongArgument("a tuple of length " \ 02320 + str(len(elt)), 02321 "a tuple of length 2") 02322 02323 if not ((isinstance(elt[0], Calculable) \ 02324 and elt[0].is_literal() 02325 ) \ 02326 or elt[0] is None): 02327 #___ 02328 raise error.WrongArgument(str(elt[0]), 02329 "None|literalCalculable") 02330 02331 if not ((isinstance(elt[1], Calculable) \ 02332 and elt[1].is_literal() 02333 ) \ 02334 or elt[1] is None): 02335 #___ 02336 raise error.WrongArgument(str(type(elt[1])), 02337 "None|literalCalculable") 02338 02339 if elt[0] in literals_positions: 02340 raise error.WrongArgument(elt[0].into_str() + " is already"\ 02341 " in the Table.", 02342 "it should be there only once") 02343 else: 02344 if isinstance(elt[0], Calculable) \ 02345 and not isinstance(elt[1], Calculable): 02346 #___ 02347 literals_positions[elt[0]] = col_nb 02348 02349 if elt[1] in literals_positions: 02350 raise error.WrongArgument(elt[1].into_str() + " is already"\ 02351 " in the Table.", 02352 "it should be there only once") 02353 else: 02354 if isinstance(elt[1], Calculable) \ 02355 and not isinstance(elt[0], Calculable): 02356 #___: 02357 literals_positions[elt[1]] = col_nb 02358 02359 02360 col_nb += 1 02361 02362 02363 if len(complete_cols) == 0: 02364 raise error.WrongArgument("no complete column found", 02365 "there should be at least one complete") 02366 02367 # Now everything is clean, let's set the fields 02368 self._coeff = coeff 02369 02370 second_line = [] 02371 02372 for i in range(len(first_line)): 02373 if first_line[i] is None: 02374 second_line += [None] 02375 else: 02376 second_line += [Item(Product([coeff, 02377 first_line[i]]).evaluate())] 02378 02379 data = [[], []] 02380 02381 for i in range(len(first_line)): 02382 if info[i] is None: 02383 data[0] += [first_line[i]] 02384 data[1] += [second_line[i]] 02385 02386 elif first_line[i] is None: 02387 data[0] += [info[i][0]] 02388 data[1] += [info[i][1]] 02389 02390 else: 02391 if info[i][0] is None: 02392 data[0] += [first_line[i]] 02393 if info[i][1] is None: 02394 data[1] += [second_line[i]] 02395 else: 02396 data[1] += [info[i][1]] 02397 else: 02398 data[0] += [info[i][0]] 02399 if info[i][1] is None: 02400 data[1] += [second_line[i]] 02401 else: 02402 data[1] += [info[i][1]] 02403 02404 #for i in xrange(len(data[0])): 02405 # if data[0][i] is None: 02406 # d0 = "None" 02407 # else: 02408 # d0 = data[0][i].dbg_str() 02409 02410 # if data[1][i] is None: 02411 # d1 = "None" 02412 # else: 02413 # d1 = data[1][i].dbg_str() 02414 02415 # print "data[0][" + str(i) + "] = " + d0 + "\n" 02416 # print "data[1][" + str(i) + "] = " + d1 + "\n" 02417 02418 Table.__init__(self, data) 02419 02420 for elt in literals_positions: 02421 col_ref = literals_positions[elt] 02422 distance = len(data[0]) 02423 final_col = None 02424 02425 for i in range(len(complete_cols)): 02426 if maths_lib.abs(complete_cols[i] - col_ref) <= distance: 02427 final_col = complete_cols[i] 02428 distance = maths_lib.abs(complete_cols[i] - col_ref) 02429 02430 literals_positions[elt] = (col_ref, final_col) 02431 02432 self._crossproducts_info = literals_positions 02433 02434 02435 02436 02437 # -------------------------------------------------------------------------- 02438 ## 02439 # @brief Returns the Table's coefficient 02440 def get_coeff(self): 02441 return self._coeff 02442 02443 02444 02445 02446 02447 # -------------------------------------------------------------------------- 02448 ## 02449 # @brief Returns the info about Cross Products 02450 def get_crossproducts_info(self): 02451 return self._crossproducts_info 02452 02453 02454 02455 02456 02457 coeff = property(get_coeff, 02458 doc = "the coefficient of the Table_UP") 02459 02460 crossproducts_info = property(get_crossproducts_info, 02461 doc = "infos about the cross products") 02462 # for instance, {'EF' : (2,0), "GH" : (3,0)} means Item 'EF' can 02463 # be calculated by a CrossProduct using columns 2 and 0, etc. 02464 02465 02466 02467 02468 02469 # -------------------------------------------------------------------------- 02470 ## 02471 # @argument arg is expected to be an object that exists in the cp info 02472 # @brief Returns the CrossProductEquation matching the given arg 02473 def into_crossproduct_equation(self, arg): 02474 if not arg in self.crossproducts_info: 02475 raise error.WrongArgument(str(arg), "an object expected to exist" \ 02476 + "in self.crossproducts_info") 02477 02478 col0 = self.crossproducts_info[arg][0] 02479 col1 = self.crossproducts_info[arg][1] 02480 02481 col_temp = col1 02482 02483 if col0 > col1: 02484 col1 = col0 02485 col0 = col_temp 02486 02487 return CrossProductEquation((self.cell[0][col0], 02488 self.cell[0][col1], 02489 self.cell[1][col0], 02490 self.cell[1][col1] 02491 )) 02492 02493 02494 02495 02496 02497 02498 02499 02500 02501 02502 02503