mathmaker  0.6(alpha)
mamk_misc/doc/mathmaker4doxygen/core/calculus.py
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