mathmaker  0.4(alpha)
mathmaker_dev/sheet/exercise/question/Q_Calculation.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 from lib import *
00024 from Q_Structure import Q_Structure
00025 from core.base_calculus import *
00026 from core.calculus import *
00027 
00028 AVAILABLE_Q_KIND_VALUES = \
00029     {'fraction_simplification' : ['default'],
00030      'fractions_sum' : ['default'],
00031      'fractions_product' : ['default'],
00032      'fractions_quotient' : ['default']
00033 
00034     }
00035 
00036 FRACTION_PRODUCT_AND_QUOTIENT_TABLE = [0.07, #2
00037                                        0.08, #3
00038                                        0.08, #4
00039                                        0.08, #5
00040                                        0.08, #6
00041                                        0.07, #7
00042                                        0.08, #8
00043                                        0.08, #9
00044                                        0.09, #10
00045                                        0.02, #11
00046                                        0.05, #12
00047                                        0.02, #13
00048                                        0.05, #14
00049                                        0.03, #15
00050                                        0.04, #16
00051                                        0.01, #17
00052                                        0.03, #18
00053                                        0.01, #19
00054                                        0.03  #20
00055                                       ]
00056 
00057 FRACTIONS_SUMS_TABLE = [([1, 2], 15),
00058                         ([1, 3], 15),
00059                         ([1, 4], 15),
00060                         ([1, 5], 15),
00061                         ([1, 6], 15),
00062                         ([1, 7], 15),
00063                         ([2, 3], 17),
00064                         ([2, 5], 10),
00065                         ([2, 7], 7),
00066                         ([3, 4], 9),
00067                         ([3, 5], 7),
00068                         ([3, 7], 5),
00069                         ([4, 5], 5),
00070                         ([4, 7], 3),
00071                         ([5, 6], 4),
00072                         ([5, 7], 4),
00073                         ([6, 7], 3)]
00074 
00075 FRACTIONS_SUMS_SCALE_TABLE = [0.02,  #(1, 2)
00076                               0.02,
00077                               0.02,
00078                               0.01,
00079                               0.005,
00080                               0.005,  #(1, 7)
00081                               0.21,  #(2, 3)
00082                               0.14,  #(2, 5)
00083                               0.02,  #(2, 7)
00084                               0.21,  #(3, 4)
00085                               0.14,  #(3, 5)
00086                               0.01,  #(3, 7)
00087                               0.16,  #(4, 5)
00088                               0.01,  #(4, 7)
00089                               0.01,  #(5, 6)
00090                               0.005,  #(5, 7)
00091                               0.005]  #(6, 7)
00092 
00093 # ------------------------------------------------------------------------------
00094 # --------------------------------------------------------------------------
00095 # ------------------------------------------------------------------------------
00096 ##
00097 # @class Q_Calculation
00098 # @brief All kinds of numeric calculation questions (Fractions Sums,
00099 # Products, numeric Sums & Products with priorities etc.)
00100 class Q_Calculation(Q_Structure):
00101 
00102 
00103 
00104 
00105 
00106     # --------------------------------------------------------------------------
00107     ##
00108     #   @brief Constructor.
00109     #   @param embedded_machine The machine to be used
00110     #   @param **options Any options
00111     #   @return One instance of question.Q_Calculation
00112     def __init__(self, embedded_machine, q_kind='default_nothing', **options):
00113         self.derived = True
00114 
00115         # The call to the mother class __init__() method will set the
00116         # fields matching optional arguments which are so far :
00117         # self.q_kind, self.q_subkind
00118         # plus self.machine, self.options (modified)
00119         Q_Structure.__init__(self, embedded_machine,
00120                              q_kind, AVAILABLE_Q_KIND_VALUES,
00121                              **options)
00122         # The purpose of this next line is to get the possibly modified
00123         # value of **options
00124         options = self.options
00125 
00126         # That's the number of the question, not of the expressions it might
00127         # contain !
00128         self.number = ""
00129         if 'number_of_questions' in options:
00130             self.number = options['number_of_questions']
00131 
00132         self.objct = None
00133 
00134         # 1st OPTION
00135         if q_kind == 'fraction_simplification':
00136             root = randomly.integer(2,
00137                                     19,
00138                                     weighted_table=[0.225, 0.225, 0, 0.2,
00139                                                     0, 0.2, 0, 0, 0, 0.07,
00140                                                     0, 0.0375, 0, 0, 0,
00141                                                     0.0375, 0, 0.005
00142                                                     ])
00143 
00144             factors_list = [j+1 for j in xrange(10)]
00145 
00146             ten_power_factor1 = 1
00147             ten_power_factor2 = 1
00148 
00149             if 'with_ten_powers' in options \
00150                and is_.a_number(options['with_ten_powers']) \
00151                and options['with_ten_powers'] <= 1 \
00152                and options['with_ten_powers'] >= 0:
00153             #___
00154                 if randomly.decimal_0_1() < options['with_ten_powers']:
00155                     ten_powers_list = [10, 10, 100, 100]
00156                     ten_power_factor1 = randomly.pop(ten_powers_list)
00157                     ten_power_factor2 = randomly.pop(ten_powers_list)
00158 
00159 
00160 
00161             self.objct = Fraction(('+',
00162                                    root*randomly.pop(factors_list) \
00163                                      * ten_power_factor1,
00164                                    root*randomly.pop(factors_list) \
00165                                      * ten_power_factor2
00166                                    ))
00167 
00168         # 2d & 3d OPTIONS
00169         # Fractions Products | Quotients
00170         elif q_kind == 'fractions_product' \
00171              or q_kind == 'fractions_quotient':
00172         #___
00173             # In some cases, the fractions will be generated
00174             # totally randomly
00175             if randomly.decimal_0_1() < 0:
00176                 lil_box = [n+2 for n in xrange(18)]
00177                 a = randomly.pop(lil_box,
00178                                  weighted_table= \
00179                                         FRACTION_PRODUCT_AND_QUOTIENT_TABLE)
00180                 b = randomly.pop(lil_box,
00181                                  weighted_table= \
00182                                         FRACTION_PRODUCT_AND_QUOTIENT_TABLE)
00183 
00184                 lil_box = [n+2 for n in xrange(18)]
00185                 c = randomly.pop(lil_box,
00186                                  weighted_table= \
00187                                         FRACTION_PRODUCT_AND_QUOTIENT_TABLE)
00188                 d = randomly.pop(lil_box,
00189                                  weighted_table= \
00190                                         FRACTION_PRODUCT_AND_QUOTIENT_TABLE)
00191 
00192                 f1 = Fraction((randomly.sign(plus_signs_ratio=0.75),
00193                                Item((randomly.sign(plus_signs_ratio=0.80),
00194                                      a)),
00195                                Item((randomly.sign(plus_signs_ratio=0.80),
00196                                      b))
00197                               ))
00198 
00199                 f2 = Fraction((randomly.sign(plus_signs_ratio=0.75),
00200                                Item((randomly.sign(plus_signs_ratio=0.80),
00201                                      c)),
00202                                Item((randomly.sign(plus_signs_ratio=0.80),
00203                                      d))
00204                               ))
00205 
00206                 #f1 = f1.simplified()
00207                 #f2 = f2.simplified()
00208 
00209             # In all other cases (80%), we'll define a "seed" a plus two
00210             # randomly numbers i and j to form the Product | Quotient :
00211             # a×i / b  ×   c / a × j
00212             # Where b is a randomly number coprime to a×i
00213             # and c is a randomly number coprime to a×j
00214             else:
00215                 a = randomly.integer(2, 8)
00216                 lil_box = [i+2 for i in xrange(7)]
00217                 i = randomly.pop(lil_box)
00218                 j = randomly.pop(lil_box)
00219 
00220                 b = randomly.coprime_to(a*i, [n+2 for n in xrange(15)])
00221                 c = randomly.not_coprime_to(b,
00222                                             [n+2 for n in xrange(30)],
00223                                             excepted=a*j)
00224 
00225                 f1 = Fraction((randomly.sign(plus_signs_ratio=0.75),
00226                                Item((randomly.sign(plus_signs_ratio=0.80),
00227                                      a*i)),
00228                                Item((randomly.sign(plus_signs_ratio=0.80),
00229                                      b))
00230                               ))
00231 
00232                 f2 = Fraction((randomly.sign(plus_signs_ratio=0.75),
00233                                Item((randomly.sign(plus_signs_ratio=0.80),
00234                                      c)),
00235                                Item((randomly.sign(plus_signs_ratio=0.80),
00236                                      a*j))
00237                               ))
00238 
00239                 if randomly.heads_or_tails():
00240                     f3 = f1.clone()
00241                     f1 = f2.clone()
00242                     f2 = f3.clone()
00243 
00244                 if q_kind == 'fractions_quotient':
00245                     f2 = f2.invert()
00246 
00247             if q_kind == 'fractions_product':
00248                 self.objct = Product([f1, f2])
00249 
00250             elif q_kind == 'fractions_quotient':
00251                 self.objct = Quotient(('+', f1, f2, 1,
00252                                        'use_divide_symbol'
00253                                      ))
00254 
00255 
00256         # 4th OPTION
00257         # Fractions Sums
00258         elif q_kind == 'fractions_sum':
00259             randomly_position = randomly.integer(0,
00260                                                  16,
00261                                                  weighted_table=\
00262                                                  FRACTIONS_SUMS_SCALE_TABLE)
00263 
00264             chosen_seed_and_generator = FRACTIONS_SUMS_TABLE[randomly_position]
00265 
00266 
00267             seed = randomly.integer(2, chosen_seed_and_generator[1])
00268 
00269             # The following test is only intended to avoid having "high"
00270             # results too often. We just check if the common denominator
00271             # will be higher than 75 (arbitrary) and if yes, we redetermine
00272             # it once. We don't do it twice since we don't want to totally
00273             # forbid high denominators.
00274             if seed * chosen_seed_and_generator[0][0] \
00275                     * chosen_seed_and_generator[0][1] >= 75:
00276             #___
00277                 seed = randomly.integer(2, chosen_seed_and_generator[1])
00278 
00279             lil_box = [0, 1]
00280             gen1 = chosen_seed_and_generator[0][lil_box.pop()]
00281             gen2 = chosen_seed_and_generator[0][lil_box.pop()]
00282 
00283             den1 = Item(gen1*seed)
00284             den2 = Item(gen2*seed)
00285 
00286             temp1 = randomly.integer(1, 20)
00287             temp2 = randomly.integer(1, 20)
00288 
00289             num1 = Item(temp1 / gcd(temp1, gen1*seed))
00290             num2 = Item(temp2 / gcd(temp2, gen2*seed))
00291 
00292             f1 = Fraction((randomly.sign(plus_signs_ratio=0.7),
00293                           num1,
00294                           den1))
00295             f2 = Fraction((randomly.sign(plus_signs_ratio=0.7),
00296                           num2,
00297                           den2))
00298 
00299             self.objct = Sum([f1.simplified(),
00300                               f2.simplified()])
00301 
00302         # 5th
00303         # still to imagine :o)
00304 
00305         # Creation of the expression :
00306         number = 0
00307         if 'expression_number' in options                                     \
00308            and is_.a_natural_int(options['expression_number']):
00309         #___
00310             number = options['expression_number']
00311         self.expression = Expression(number, self.objct)
00312 
00313 
00314 
00315     # --------------------------------------------------------------------------
00316     ##
00317     #   @brief Returns the text of the question as a str
00318     def text_to_str(self):
00319         M = self.machine
00320 
00321         return M.write_math_style1(M.type_string(self.expression))
00322 
00323 
00324 
00325 
00326 
00327 
00328 
00329     # --------------------------------------------------------------------------
00330     ##
00331     #   @brief Returns the answer of the question as a str
00332     def answer_to_str(self):
00333         M = self.machine
00334 
00335         result = ""
00336 
00337         while self.objct != None:
00338             result += M.write_math_style1(M.type_string(self.expression))
00339 
00340             self.objct = self.objct.calculate_next_step()
00341             if self.objct != None:
00342                 self.expression = Expression(self.expression.name,
00343                                                   self.objct)
00344 
00345 
00346         return result
00347 
00348 
00349 
00350 
00351 
00352 
00353