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