mathmaker
0.6(alpha)
|
00001 # -*- coding: utf-8 -*- 00002 00003 # Mathmaker creates automatically maths exercises sheets 00004 # with their answers 00005 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net> 00006 00007 # This file is part of Mathmaker. 00008 00009 # Mathmaker is free software; you can redistribute it and/or modify 00010 # it under the terms of the GNU General Public License as published by 00011 # the Free Software Foundation; either version 3 of the License, or 00012 # any later version. 00013 00014 # Mathmaker is distributed in the hope that it will be useful, 00015 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 # GNU General Public License for more details. 00018 00019 # You should have received a copy of the GNU General Public License 00020 # along with Mathmaker; if not, write to the Free Software 00021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00022 00023 import 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