mathmaker
0.4(alpha)
|
00001 # -*- coding: utf-8 -*- 00002 00003 # Mathmaker creates automatically maths exercises sheets 00004 # with their answers 00005 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net> 00006 00007 # This file is part of Mathmaker. 00008 00009 # Mathmaker is free software; you can redistribute it and/or modify 00010 # it under the terms of the GNU General Public License as published by 00011 # the Free Software Foundation; either version 3 of the License, or 00012 # any later version. 00013 00014 # Mathmaker is distributed in the hope that it will be useful, 00015 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 # GNU General Public License for more details. 00018 00019 # You should have received a copy of the GNU General Public License 00020 # along with Mathmaker; if not, write to the Free Software 00021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00022 00023 import math 00024 from decimal import Decimal 00025 00026 from lib import * 00027 from lib import utils 00028 from lib.common import alphabet 00029 from lib.common import pythagorean 00030 from lib.common import vocabulary 00031 from lib.common.cst import * 00032 from lib.maths_lib import * 00033 from Q_Structure import Q_Structure 00034 #from core.calculus import Equality 00035 from core.base_calculus import * 00036 from core.calculus import * 00037 #from core.base_geometry import * 00038 from core.geometry import * 00039 #from core.base import * 00040 00041 00042 AVAILABLE_Q_KIND_VALUES = {'pythagorean_theorem' : ['calculate_hypotenuse', 00043 'calculate_one_leg'], 00044 'converse_of_pythagorean_theorem' : ['default'], 00045 'contrapositive_of_pythagorean_theorem': ['default'], 00046 'cosinus' : ['calculate_hypotenuse', 00047 'calculate_one_leg', 00048 'calculate_angle'], 00049 'sinus' : ['calculate_hypotenuse', 00050 'calculate_one_leg', 00051 'calculate_angle'], 00052 'tangente' : ['calculate_hypotenuse', 00053 'calculate_one_leg', 00054 'calculate_angle'], 00055 } 00056 00057 00058 00059 # ------------------------------------------------------------------------------ 00060 # -------------------------------------------------------------------------- 00061 # ------------------------------------------------------------------------------ 00062 ## 00063 # @class Q_RightTriangle 00064 # @brief All questions about the right triangle 00065 class Q_RightTriangle(Q_Structure): 00066 00067 00068 00069 00070 00071 # -------------------------------------------------------------------------- 00072 ## 00073 # @brief Constructor. 00074 # @param embedded_machine The machine to be used 00075 # @options 00076 # @return One instance of question.Q_RightTriangle 00077 def __init__(self, embedded_machine, q_kind='default_nothing', **options): 00078 self.derived = True 00079 00080 # The call to the mother class __init__() method will set the 00081 # fields matching optional arguments which are so far : 00082 # self.q_kind, self.q_subkind 00083 # plus self.machine, self.options (modified) 00084 Q_Structure.__init__(self, embedded_machine, 00085 q_kind, AVAILABLE_Q_KIND_VALUES, 00086 **options) 00087 # The purpose of this next line is to get the possibly modified 00088 # value of **options 00089 options = self.options 00090 00091 00092 00093 # Set the default values of the different options 00094 use_pythagorean_triples = False 00095 if ('use_pythagorean_triples' in options \ 00096 and options['use_pythagorean_triples'] in YES) \ 00097 or (self.q_kind == 'converse_of_pythagorean_theorem') : 00098 #___ 00099 use_pythagorean_triples = True 00100 00101 use_decimals = True 00102 if 'use_decimals' in options \ 00103 and not options['use_decimals'] in YES: 00104 #___ 00105 use_decimals = False 00106 00107 self.round_to = "" 00108 00109 if 'round_to' in options and options['round_to'] in PRECISION: 00110 self.round_to = options['round_to'] 00111 00112 if not use_pythagorean_triples: 00113 if self.round_to == "": 00114 if use_decimals: 00115 self.round_to = HUNDREDTH 00116 else: 00117 self.round_to = TENTH 00118 00119 self.use_pythagorean_triples = use_pythagorean_triples 00120 00121 self.figure_in_the_text = True 00122 00123 if 'figure_in_the_text' in options \ 00124 and not options['figure_in_the_text'] in YES: 00125 #___ 00126 self.figure_in_the_text = False 00127 00128 rotation_option = 'no' 00129 00130 if 'rotate_around_barycenter' in options: 00131 rotation_option = options['rotate_around_barycenter'] 00132 00133 self.final_unit = "" 00134 00135 if 'final_unit' in options \ 00136 and options['final_unit'] in LENGTH_UNITS: 00137 #___ 00138 self.final_unit = options['final_unit'] 00139 00140 sides_units = [self.final_unit, 00141 self.final_unit, 00142 self.final_unit] 00143 00144 # Later, allow to use a different length unit for the sides 00145 # than the final expected unit ; allow different units for different 00146 # sides (for instance giving a list in option 'sides_units')... 00147 # So far we will do with only ONE unit 00148 #if 'sides_units' in options \ 00149 # and options['sides_units'] in LENGTH_UNITS: 00150 ##___ 00151 # sides_units = options['sides_units'] 00152 00153 self.right_triangle = None 00154 00155 self.unknown_side = None 00156 self.known_sides = [] 00157 00158 00159 # Now set some randomly values 00160 letters = [elt for elt in alphabet.UPPERCASE] 00161 00162 vertices_names = (randomly.pop(letters), 00163 randomly.pop(letters), 00164 randomly.pop(letters)) 00165 00166 00167 # Here you can begin to write code for the different 00168 # q_kinds & q_subkinds 00169 if self.q_kind == 'pythagorean_theorem': 00170 sides_values = [None, None, None] 00171 00172 if use_pythagorean_triples: 00173 triples = pythagorean.ALL_TRIPLES_5_100 00174 00175 if use_decimals: 00176 triples = pythagorean.ALL_TRIPLES_5_100 \ 00177 + pythagorean.TRIPLES_101_200_WO_TEN_MULTIPLES 00178 00179 sides_values = randomly.pop(triples) 00180 00181 if use_decimals: 00182 sides_values = [Decimal(str(Decimal(sides_values[0])/10)), 00183 Decimal(str(Decimal(sides_values[1])/10)), 00184 Decimal(str(Decimal(sides_values[2])/10)) 00185 ] 00186 00187 if self.q_subkind == 'calculate_hypotenuse': 00188 sides_values[2] = "" 00189 sides_units[2] = "" 00190 00191 else: 00192 # case : self.q_subkind == 'calculate_one_leg' 00193 leg0_or_1 = randomly.pop([0, 1]) 00194 sides_values[leg0_or_1] = "" 00195 sides_units[leg0_or_1] = "" 00196 00197 else: 00198 # NO pythagorean triples. 00199 # The two generated values must NOT match any pythagorean 00200 # triple 00201 if use_decimals: 00202 min_side_value = 5 00203 max_side_value = 200 00204 else: 00205 min_side_value = 5 00206 max_side_value = 100 00207 00208 if self.q_subkind == 'calculate_hypotenuse': 00209 first_leg = randomly.integer(min_side_value, 00210 max_side_value) 00211 00212 # we will take the leg values between 00213 # at least 25% and at most 150% of the length of first leg 00214 # (and smaller than max_side_value) 00215 second_leg_values = [] 00216 for i in xrange(int(first_leg*1.5)): 00217 if i+int(first_leg*0.25) <= 1.5*first_leg \ 00218 and i+int(first_leg*0.25) <= max_side_value: 00219 #___ 00220 second_leg_values += [i+int(first_leg*0.25)] 00221 00222 second_leg_unauthorized_values = \ 00223 pythagorean.get_legs_matching_given_leg(first_leg) 00224 00225 second_leg_possible_values = utils.take_away(\ 00226 second_leg_values, 00227 second_leg_unauthorized_values) 00228 00229 if randomly.heads_or_tails(): 00230 sides_values = [first_leg, 00231 randomly.pop(second_leg_possible_values), 00232 "" 00233 ] 00234 sides_units[2] = "" 00235 00236 else: 00237 sides_values = [randomly.pop(second_leg_possible_values), 00238 first_leg, 00239 "" 00240 ] 00241 sides_units[2] = "" 00242 00243 else: 00244 # case : self.q_subkind == 'calculate_one_leg' 00245 00246 hypotenuse = randomly.integer(min_side_value, 00247 max_side_value) 00248 00249 # we will take the leg values between 00250 # at least 25% and at most 90% of the length of hypotenuse 00251 # to avoid "weird" cases (with a very subtle difference 00252 # between the given values and the one to calculate) 00253 leg_values = [] 00254 for i in xrange(int(hypotenuse*0.9)): 00255 if i+int(hypotenuse*0.25) <= 0.9*hypotenuse: 00256 leg_values += [i+int(hypotenuse*0.25)] 00257 00258 leg_unauthorized_values = \ 00259 pythagorean.get_legs_matching_given_hypotenuse(\ 00260 hypotenuse) 00261 00262 leg_possible_values = utils.take_away(leg_values, 00263 leg_unauthorized_values) 00264 00265 if randomly.heads_or_tails(): 00266 sides_values = ["", 00267 randomly.pop(leg_possible_values), 00268 hypotenuse 00269 ] 00270 sides_units[0] = "" 00271 00272 else: 00273 sides_values = [randomly.pop(leg_possible_values), 00274 "", 00275 hypotenuse 00276 ] 00277 sides_units[1] = "" 00278 00279 self.right_triangle = \ 00280 RightTriangle((vertices_names, 00281 'sketch' 00282 ), 00283 rotate_around_isobarycenter=rotation_option 00284 ) 00285 00286 self.right_triangle.leg0.set_label(Value(sides_values[0], 00287 unit=sides_units[0]) 00288 ) 00289 self.right_triangle.leg1.set_label(Value(sides_values[1], 00290 unit=sides_units[1]) 00291 ) 00292 self.right_triangle.hypotenuse.set_label(Value(sides_values[2], 00293 unit=sides_units[2]) 00294 ) 00295 00296 for side in self.right_triangle.sides: 00297 if side.label.raw_value == "": 00298 self.unknown_side = side.clone() 00299 else: 00300 self.known_sides += [side.clone()] 00301 00302 00303 00304 elif self.q_kind == 'converse_of_pythagorean_theorem' \ 00305 or self.q_kind == 'contrapositive_of_pythagorean_theorem': 00306 #___ 00307 sides_values = [None, None, None] 00308 triples = pythagorean.ALL_TRIPLES_5_100 00309 00310 if use_decimals: 00311 triples = pythagorean.ALL_TRIPLES_5_100 \ 00312 + pythagorean.TRIPLES_101_200_WO_TEN_MULTIPLES 00313 00314 sides_values = randomly.pop(triples) 00315 00316 if self.q_kind == 'contrapositive_of_pythagorean_theorem': 00317 # We'll change exactly one value to be sure the triplet 00318 # is NOT pythagorean 00319 if randomly.heads_or_tails(): 00320 # We will decrease the lowest value 00321 max_delta = int(0.1 * sides_values[0]) 00322 min_delta = 1 00323 if min_delta > max_delta: 00324 max_delta = min_delta 00325 chosen_delta = randomly.pop(\ 00326 [i+min_delta for i in xrange(max_delta-min_delta+1)]) 00327 00328 sides_values = [sides_values[0]-chosen_delta, 00329 sides_values[1], 00330 sides_values[2] 00331 ] 00332 00333 else: 00334 # We will increase the highest value 00335 max_delta = int(0.1 * sides_values[2]) 00336 min_delta = 1 00337 if min_delta > max_delta: 00338 max_delta = min_delta 00339 chosen_delta = randomly.pop(\ 00340 [i+min_delta for i in xrange(max_delta-min_delta+1)]) 00341 00342 sides_values = [sides_values[0], 00343 sides_values[1], 00344 sides_values[2]+chosen_delta 00345 ] 00346 00347 if use_decimals: 00348 sides_values = [Decimal(str(Decimal(sides_values[0])/10)), 00349 Decimal(str(Decimal(sides_values[1])/10)), 00350 Decimal(str(Decimal(sides_values[2])/10)) 00351 ] 00352 00353 00354 self.right_triangle = \ 00355 RightTriangle((vertices_names, 00356 'sketch' 00357 ), 00358 rotate_around_isobarycenter=rotation_option 00359 ) 00360 00361 self.right_triangle.leg0.set_label(Value(sides_values[0], 00362 unit=sides_units[0]) 00363 ) 00364 self.right_triangle.leg1.set_label(Value(sides_values[1], 00365 unit=sides_units[1]) 00366 ) 00367 self.right_triangle.hypotenuse.set_label(Value(sides_values[2], 00368 unit=sides_units[2]) 00369 ) 00370 00371 self.right_triangle.right_angle.set_mark("") 00372 00373 00374 00375 00376 00377 00378 00379 00380 00381 00382 00383 00384 00385 # -------------------------------------------------------------------------- 00386 ## 00387 # @brief Returns the text of the question as a str 00388 def text_to_str(self): 00389 M = self.machine 00390 result = self.displayable_number 00391 00392 if self.q_kind == 'pythagorean_theorem': 00393 if self.figure_in_the_text: 00394 result += M.insert_picture(self.right_triangle) 00395 00396 else: 00397 result += _("The triangle %(triangle_name)s has a right \ 00398 angle in %(right_vertex)s.") \ 00399 % {'triangle_name' : str(self.right_triangle.name), 00400 'right_vertex' : str(self.right_triangle.vertex1.name) 00401 } 00402 result += " " + str(self.known_sides[0].length_name) \ 00403 + " = " \ 00404 + self.known_sides[0].label.into_str(display_unit='yes')\ 00405 + ". " \ 00406 + str(self.known_sides[1].length_name) \ 00407 + " = " \ 00408 + self.known_sides[1].label.into_str(display_unit='yes')\ 00409 + ". " + M.write_new_line() 00410 00411 result += _("Calculate the length of") \ 00412 + " " \ 00413 + self.unknown_side.name \ 00414 + "." 00415 00416 if self.final_unit != "": 00417 result += " " + _("Give the result in") + " " \ 00418 + self.final_unit + "." 00419 00420 if self.round_to != "": 00421 result += " " + _("Round the result") + " " \ 00422 + vocabulary.PRECISION_IDIOMS[self.round_to] + "." 00423 00424 elif self.q_kind == 'converse_of_pythagorean_theorem' \ 00425 or self.q_kind == 'contrapositive_of_pythagorean_theorem': 00426 #___ 00427 if self.figure_in_the_text: 00428 result += M.insert_picture(self.right_triangle) 00429 00430 else: 00431 sides_copy = [self.right_triangle.sides[0].clone(), 00432 self.right_triangle.sides[1].clone(), 00433 self.right_triangle.sides[2].clone() 00434 ] 00435 side0 = randomly.pop(sides_copy) 00436 side1 = randomly.pop(sides_copy) 00437 side2 = randomly.pop(sides_copy) 00438 00439 result += _("%(triangle_name)s is a triangle such as")\ 00440 %{'triangle_name' : str(self.right_triangle.name) 00441 } 00442 00443 result += " " + str(side0.length_name) 00444 result += " = " 00445 result += side0.label.into_str(display_unit=True) + ", " 00446 00447 result += str(side1.length_name) 00448 result += " = " 00449 result += side1.label.into_str(display_unit=True) 00450 result += " " 00451 result += _("and") 00452 result += " " 00453 00454 result += str(side2.length_name) 00455 result += " = " 00456 result += side2.label.into_str(display_unit=True) + "." 00457 result += " " 00458 00459 00460 result += _("Is it a right triangle ? Prove your answer and if the \ 00461 triangle is right, give the name of the right angle.") 00462 00463 result += M.write_new_line() 00464 00465 00466 return result + M.write_new_line() 00467 00468 00469 00470 00471 00472 00473 # -------------------------------------------------------------------------- 00474 ## 00475 # @brief Returns the answer of the question as a str 00476 def answer_to_str(self): 00477 M = self.machine 00478 00479 if self.q_kind == 'pythagorean_theorem': 00480 # Resolution (and the part with the figure will be dealed later) 00481 result = _("The triangle %(triangle_name)s has a right angle in \ 00482 %(right_vertex)s.") \ 00483 % {'triangle_name' : str(self.right_triangle.name), 00484 'right_vertex' : str(self.right_triangle.vertex1.name) 00485 } 00486 00487 result += M.write_new_line() 00488 00489 result += _("Then by Pythagoras theorem") + " :" 00490 00491 pyth_eq = self.right_triangle.pythagorean_substequality() 00492 00493 result += M.write_math_style1(pyth_eq.into_str()) 00494 00495 if self.use_pythagorean_triples: 00496 result += M.write(Equation(pyth_eq.substitute()).\ 00497 auto_resolution( \ 00498 dont_display_equations_name=True, 00499 pythagorean_mode='yes', 00500 unit=self.final_unit, 00501 underline_result='yes') 00502 ) 00503 00504 00505 else: 00506 result += M.write(Equation(pyth_eq.substitute()).\ 00507 auto_resolution( \ 00508 dont_display_equations_name=True, 00509 decimal_result=self.round_to, 00510 pythagorean_mode='yes', 00511 unit=self.final_unit, 00512 underline_result='yes') 00513 ) 00514 00515 00516 00517 if self.figure_in_the_text: 00518 return self.displayable_number + result 00519 else: 00520 content = [self.displayable_number \ 00521 + _("Sketch") + " :" \ 00522 + M.write_new_line() \ 00523 + M.insert_picture(self.right_triangle), 00524 result] 00525 return M.write_layout((1, 2), [9, 9], content) 00526 00527 00528 elif self.q_kind == 'converse_of_pythagorean_theorem' \ 00529 or self.q_kind == 'contrapositive_of_pythagorean_theorem': 00530 #___ 00531 hyp_equality = Equality([Item(('+', 00532 self.right_triangle.\ 00533 hypotenuse.length_name, 00534 2)), 00535 Item(('+', 00536 self.right_triangle.\ 00537 hypotenuse.label.raw_value, 00538 2)) 00539 ]) 00540 hyp_equality_step2 = Equality([Item(('+', 00541 self.right_triangle.\ 00542 hypotenuse.length_name, 00543 2)), 00544 Item(Item(('+', 00545 self.right_triangle.\ 00546 hypotenuse.label.raw_value, 00547 2)).evaluate() 00548 ) 00549 ]) 00550 00551 legs_equality = Equality([Sum([Item(('+', 00552 self.right_triangle.leg0.length_name, 00553 2)), 00554 Item(('+', 00555 self.right_triangle.leg1.length_name, 00556 2)) 00557 ]), 00558 Sum([Item(('+', 00559 self.right_triangle.leg0.label.raw_value, 00560 2)), 00561 Item(('+', 00562 self.right_triangle.leg1.label.raw_value, 00563 2)) 00564 ]) 00565 ]) 00566 legs_equality_step2 = Equality([\ 00567 Sum([Item(('+', 00568 self.right_triangle.leg0.length_name, 00569 2)), 00570 Item(('+', 00571 self.right_triangle.leg1.length_name, 00572 2)) 00573 ]), 00574 Item(Sum([Item(('+', 00575 self.right_triangle.leg0.label.raw_value, 00576 2)), 00577 Item(('+', 00578 self.right_triangle.leg1.label.raw_value, 00579 2)) 00580 ]).evaluate() 00581 ) 00582 ]) 00583 00584 00585 result = _("On one hand:") + M.write_new_line() 00586 result += M.write_math_style1(hyp_equality.into_str()) 00587 result += M.write_math_style1(hyp_equality_step2.into_str()) 00588 00589 result += _("On the other hand:") + M.write_new_line() 00590 result += M.write_math_style1(legs_equality.into_str()) 00591 result += M.write_math_style1(legs_equality_step2.into_str()) 00592 00593 result += _("Hence:") 00594 00595 if self.q_kind == 'converse_of_pythagorean_theorem': 00596 result += M.write_math_style1(\ 00597 self.right_triangle.pythagorean_equality().into_str()) 00598 result += _("So, by the converse of the pythagorean theorem,") 00599 #result += M.write_new_line() 00600 result += " " 00601 result += _("%(triangle_name)s has a right angle\ 00602 in %(right_vertex)s.")\ 00603 % {'triangle_name' : str(self.right_triangle.name), 00604 'right_vertex' : str(self.right_triangle.vertex1.name) 00605 } 00606 00607 elif self.q_kind == 'contrapositive_of_pythagorean_theorem': 00608 result += M.write_math_style1(\ 00609 self.right_triangle.pythagorean_equality(\ 00610 equal_signs=['neq']).into_str()) 00611 00612 result += _("So, by the contrapositive of the pythagorean\ 00613 theorem,") 00614 #result += M.write_new_line() 00615 result += " " 00616 result += _("%(triangle_name)s has no right angle.")\ 00617 % {'triangle_name' : str(self.right_triangle.name) 00618 } 00619 00620 00621 00622 if self.figure_in_the_text: 00623 return self.displayable_number + result 00624 else: 00625 content = [self.displayable_number \ 00626 + _("Sketch") + " :" \ 00627 + M.write_new_line() \ 00628 + M.insert_picture(self.right_triangle), 00629 result] 00630 return M.write_layout((1, 2), [6, 12], content) 00631 00632 00633 00634 00635 00636