mathmaker  0.6(alpha)
mamk_misc/doc/mathmaker4doxygen/lib/utils.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 core.base_calculus
00024 import error
00025 from decimal import Decimal
00026 
00027 
00028 
00029 
00030 
00031 # --------------------------------------------------------------------------
00032 ##
00033 #   @brief Returns the reduced Product made from the given literals list
00034 #   For example, [x, x², x**6] would return [x**9].
00035 #   Notice that the Items' sign isn't managed here
00036 #   @param provided_list The literal Items list
00037 def reduce_literal_items_product(provided_list):
00038     aux_dict = {}
00039 
00040     if not isinstance(provided_list, list):
00041         raise error.UncompatibleType(provided_list, "A list")
00042 
00043     for i in range(len(provided_list)):
00044         if isinstance(provided_list[i], core.base_calculus.Item):
00045             if provided_list[i].is_literal():
00046                 if not provided_list[i].raw_value in aux_dict:
00047                     aux_dict[provided_list[i].raw_value] =                        \
00048                                                 provided_list[i].exponent.raw_value
00049                 else:
00050                     aux_dict[provided_list[i].raw_value] +=                       \
00051                                                 provided_list[i].exponent.raw_value
00052         elif isinstance(provided_list[i], core.base_calculus.Product):
00053             if len(provided_list[i].factor) == 1:
00054                 if isinstance(provided_list[i].factor[0],
00055                               core.base_calculus.Item):
00056                     if provided_list[i].factor[0].is_literal():
00057                         if not provided_list[i].factor[0].raw_value in aux_dict:
00058                             aux_dict[provided_list[i].factor[0].raw_value] =      \
00059                                           provided_list[i].exponent           \
00060                                           * provided_list[i].factor[0].exponent
00061                         else:
00062                             aux_dict[provided_list[i].factor[0].raw_value] +=     \
00063                                            provided_list[i].exponent          \
00064                                           * provided_list[i].factor[0].exponent
00065                     else:
00066                         raise error.UncompatibleType(                         \
00067                                               provided_list[i].factor[0],     \
00068                                               "This Item should be a literal")
00069                 else:
00070                     raise error.UncompatibleType(provided_list[i].factor[0],  \
00071                                                "This object should be an Item")
00072             else:
00073                 raise error.UncompatibleType(provided_list[i],                \
00074                                           "This Product should contain exactly\
00075                                           one object (and this should be a \
00076                                           literal Item)")
00077         else:
00078             raise error.UncompatibleType(provided_list[i],                    \
00079                                        "A literal Item or a Product \
00080                                        containing only one literal Item")
00081 
00082     reduced_list = []
00083 
00084     for key in aux_dict:
00085         aux_item = core.base_calculus.Item(('+', key, aux_dict[key]))
00086         reduced_list.append(aux_item)
00087 
00088     return reduced_list
00089 
00090 
00091 
00092 
00093 
00094 # --------------------------------------------------------------------------
00095 ##
00096 #   @brief A substitute for append() in a special case, for dictionaries
00097 #   This method checks if a "provided_key" is already in the lexicon.
00098 #   If yes, then the associated_coeff term is added to the matching Sum.
00099 #   If not, the key is created and a Sum is associated which will contain
00100 #   associated_coeff as only term.
00101 #   @param  provided_key The key to use
00102 #   @param  associated_coeff The value to save in the or add to the Sum
00103 #   @param  lexi The dictionary where to put the couple key/coeff
00104 def put_term_in_lexicon(provided_key, associated_coeff, lexi):
00105     # This flag will remain False as long as the key isn't found in the lexicon
00106     key_s_been_found = False
00107 
00108     # We check the lexicon's keys one after the other
00109     # IMPORTANT NOTICE : the 'in' operator can't be used because it returns
00110     # True ONLY if the SAME OBJECT has been used as a key (and will return
00111     # False if another object of same kind and content is already there)
00112     for key in lexi:
00113         if provided_key == key:
00114             # it is important to use the key that was already there to put the
00115             # new coefficient in the lexicon
00116             lexi[key].append(associated_coeff)
00117             key_s_been_found = True
00118 
00119     if not key_s_been_found:
00120         new_coeff_sum = core.base_calculus.Sum([associated_coeff])
00121         lexi[provided_key] = new_coeff_sum
00122 
00123 
00124 
00125 
00126 
00127 # --------------------------------------------------------------------------
00128 ##
00129 #   @brief Gather all literal Values of an expression
00130 #   @param xpr The (Calculable) expression to iter over
00131 #   @return [literal Values]
00132 def gather_literals(xpr):
00133     if not isinstance(xpr, core.base_calculus.Calculable):
00134         raise error.UncompatibleType(xpr, " Calculable ")
00135 
00136     if isinstance(xpr, core.base_calculus.Value):
00137         if xpr.is_literal():
00138             return [xpr]
00139         else:
00140             return []
00141     else:
00142         result = []
00143         for elt in xpr:
00144             result += gather_literals(elt)
00145         return result
00146 
00147 
00148 
00149 
00150 
00151 # --------------------------------------------------------------------------
00152 ##
00153 #   @brief Purges the given list from duplicates elements
00154 #   @param l The list
00155 #   @return [list purged from duplicates elements]
00156 def purge_from_duplicates(l):
00157     result = []
00158 
00159     for elt in l:
00160         if not elt in result:
00161             result += [elt]
00162 
00163     return result
00164 
00165 
00166 
00167 
00168 # --------------------------------------------------------------------------
00169 ##
00170 #   @brief Checks if the literals of a *Equality|*Expression can be replaced
00171 #   @param objcts The list of literal expressions
00172 #   @param subst_dict The dictionnary to use
00173 #   @param how_many To tell if all the literals must be replaced
00174 #                   ('all'|'all_but_one'|'at_least_one'|int)
00175 #   @return True|False
00176 def check_lexicon_for_substitution(objcts, subst_dict, how_many):
00177     literals_list = []
00178 
00179     if not (how_many == 'all' or how_many == 'all_but_one' \
00180             or how_many == 'at_least_one' or type(how_many) == int):
00181     #___
00182         raise error.UncompatibleType(how_many, " 'all'|'all_but_one'" \
00183                                                + "|'at_least_one'|int ")
00184 
00185     if not type(objcts) == list:
00186         raise error.UncompatibleType(objcts, " list ")
00187 
00188     for elt in objcts:
00189         if not isinstance(elt, core.base_calculus.Calculable):
00190             raise error.UncompatibleType(elt, " Calculable ")
00191 
00192     if not type(subst_dict) == dict:
00193         raise error.UncompatibleType(subst_dict, " dict ")
00194 
00195     for elt in subst_dict:
00196         if not (isinstance(elt, core.base_calculus.Value) \
00197                 and elt.is_literal() \
00198                 and isinstance(subst_dict[elt], core.base_calculus.Value) \
00199                 and subst_dict[elt].is_numeric()
00200                 ):
00201         #___
00202             raise error.WrongArgument(subst_dict, " this dict should contain " \
00203                                             + "only " \
00204                                             + "literal Value: numeric Value")
00205 
00206     # Make the list of all literals
00207     for xpr in objcts:
00208         literals_list += gather_literals(xpr)
00209 
00210     #debug_str = ""
00211     #for l in literals_list:
00212     #    debug_str += l.dbg_str() + " "
00213     #print "debug : literals_list = " + debug_str + "\n"
00214 
00215     literals_list = purge_from_duplicates(literals_list)
00216     #literals_list_bis = []
00217     #for elt in literals_list:
00218     #    literals_list_bis += [elt.clone()]
00219 
00220     #debug_str = ""
00221     #for l in literals_list:
00222     #    debug_str += l.dbg_str() + " "
00223     #print "debug : literals_list after purging = " + debug_str + "\n"
00224 
00225     subst_dict_copy = {}
00226     for key in subst_dict:
00227         subst_dict_copy[key] = subst_dict[key].clone()
00228 
00229     #for k in subst_dict_copy:
00230     #    print "debug : subst_dict_copy[" + k.dbg_str() + "] = " + subst_dict_copy[k].dbg_str() + "\n"
00231 
00232     # Now check if the literals of the expressions are all in the lexicon
00233     n = 0
00234     N = len(literals_list)
00235     #collected_is= []
00236     for i in range(len(literals_list)):
00237         #print "debug : literals_list[" + str(i) + "] = " + literals_list[i].dbg_str() + " is in subst_dict_copy ? " + str(literals_list[i] in subst_dict_copy) + "\n"
00238         #if i >= 1:
00239             #print "debug : checking the keys... "
00240         collected_keys = []
00241         for key in subst_dict_copy:
00242             #print "debug : key = " + k.dbg_str() + " key == literals_list[" + str(i) + "] ? " + str(k == literals_list[i])
00243             if key == literals_list[i]:
00244                 n += 1
00245                 #collected_is += [i]
00246                 collected_keys += [key]
00247         for k in collected_keys:
00248             subst_dict_copy.pop(k)
00249 
00250     #for i in collected_is:
00251     #    literals_list.pop(i)
00252 
00253 
00254         #if literals_list[i] in subst_dict_copy:
00255         #    print "debug : found one elt \n"
00256         #    n += 1
00257         #    literals_list.pop(i)
00258         #    subst_dict_copy.pop(literals_list[i])
00259 
00260     if not len(subst_dict_copy) == 0:
00261         #print "debug : quitting here\n"
00262         return False
00263 
00264     #print "debug: how_many = " + str(how_many) + "\n"
00265 
00266     if how_many == 'all':
00267         return n == N
00268     elif how_many == 'all_but_one':
00269         return n == N - 1
00270     elif how_many == 'at_least_one':
00271         #print "debug: n = " + str(n) + "\n"
00272         return n >= 1
00273     else:
00274         return n == how_many
00275 
00276 
00277 
00278 
00279 
00280 # --------------------------------------------------------------------------
00281 ##
00282 #   @brief Takes all elements of a list1 away if they're in list2
00283 def take_away(list1, list2):
00284     if not (type(list1) == list and type(list2) == list):
00285         raise error.WrongArgument(str(list1) + " and " + str(list2),
00286                                   " two lists.")
00287 
00288     result = []
00289 
00290     for elt in list1:
00291         if not elt in list2:
00292             result += [elt]
00293 
00294     return result
00295 
00296 
00297 
00298 
00299 # --------------------------------------------------------------------------
00300 ##
00301 #   @brief Transforms the xE+n results in decimal form (ex. 1E+1 -> 10)
00302 def correct_normalize_results(d):
00303     if not isinstance(d, Decimal):
00304         raise error.WrongArgument(str(type(d)), "a Decimal")
00305 
00306     return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
00307 
00308 
00309 
00310