mathmaker  0.4(alpha)
mathmaker_dev/sheet/exercise/question/Q_RightTriangle.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 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