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