Source code for mathmaker.lib.old_style_sheet.exercise.question.Q_Calculation

# -*- coding: utf-8 -*-

# Mathmaker creates automatically maths exercises sheets
# with their answers
# Copyright 2006-2017 Nicolas Hainaux <nh.techn@gmail.com>

# This file is part of Mathmaker.

# Mathmaker is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# any later version.

# Mathmaker is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with Mathmaker; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import random
from math import gcd

from mathmakerlib.calculus import is_natural, is_number

from mathmaker.lib.tools.maths import coprimes_to, not_coprimes_to
from mathmaker.lib import shared
from .Q_Structure import Q_Structure
from mathmaker.lib.core.base_calculus import (Item, Fraction, Product,
                                              Quotient, Sum)
from mathmaker.lib.core.calculus import Expression

AVAILABLE_Q_KIND_VALUES = \
    {'fraction_simplification': ['default'],
     'fractions_sum': ['default'],
     'fractions_product': ['default'],
     'fractions_quotient': ['default']
     }

FRACTION_PRODUCT_AND_QUOTIENT_TABLE = [0.07,  # 2
                                       0.08,  # 3
                                       0.08,  # 4
                                       0.08,  # 5
                                       0.08,  # 6
                                       0.07,  # 7
                                       0.08,  # 8
                                       0.08,  # 9
                                       0.09,  # 10
                                       0.02,  # 11
                                       0.05,  # 12
                                       0.02,  # 13
                                       0.05,  # 14
                                       0.03,  # 15
                                       0.04,  # 16
                                       0.01,  # 17
                                       0.03,  # 18
                                       0.01,  # 19
                                       0.03   # 20
                                       ]

FRACTIONS_SUMS_TABLE = [([1, 2], 15),
                        ([1, 3], 15),
                        ([1, 4], 15),
                        ([1, 5], 15),
                        ([1, 6], 15),
                        ([1, 7], 15),
                        ([2, 3], 17),
                        ([2, 5], 10),
                        ([2, 7], 7),
                        ([3, 4], 9),
                        ([3, 5], 7),
                        ([3, 7], 5),
                        ([4, 5], 5),
                        ([4, 7], 3),
                        ([5, 6], 4),
                        ([5, 7], 4),
                        ([6, 7], 3)]

FRACTIONS_SUMS_SCALE_TABLE = [0.02,  # (1, 2)
                              0.02,
                              0.02,
                              0.01,
                              0.005,
                              0.005,  # (1, 7)
                              0.21,  # (2, 3)
                              0.14,  # (2, 5)
                              0.02,  # (2, 7)
                              0.21,  # (3, 4)
                              0.14,  # (3, 5)
                              0.01,  # (3, 7)
                              0.16,  # (4, 5)
                              0.01,  # (4, 7)
                              0.01,  # (5, 6)
                              0.005,  # (5, 7)
                              0.005]  # (6, 7)


# ------------------------------------------------------------------------------
# --------------------------------------------------------------------------
# ------------------------------------------------------------------------------
##
# @class Q_Calculation
# @brief All kinds of numeric calculation questions (Fractions Sums,
# Products, numeric Sums & Products with priorities etc.)
[docs]class Q_Calculation(Q_Structure): # -------------------------------------------------------------------------- ## # @brief Constructor. # @param **options Any options # @return One instance of question.Q_Calculation def __init__(self, q_kind='default_nothing', **options): self.derived = True # The call to the mother class __init__() method will set the # fields matching optional arguments which are so far: # self.q_kind, self.q_subkind # plus self.options (modified) Q_Structure.__init__(self, q_kind, AVAILABLE_Q_KIND_VALUES, **options) # The purpose of this next line is to get the possibly modified # value of **options options = self.options # That's the number of the question, not of the expressions it might # contain ! self.number = "" if 'number_of_questions' in options: self.number = options['number_of_questions'] self.objct = None # 1st OPTION if q_kind == 'fraction_simplification': root = random.choices([n + 2 for n in range(19 - 2 + 1)], weights=[0.225, 0.225, 0, 0.2, 0, 0.2, 0, 0, 0, 0.07, 0, 0.0375, 0, 0, 0, 0.0375, 0, 0.005])[0] ten_power_factor1 = 1 ten_power_factor2 = 1 if 'with_ten_powers' in options \ and is_number(options['with_ten_powers']) \ and options['with_ten_powers'] <= 1 \ and options['with_ten_powers'] >= 0: # __ if random.random() < options['with_ten_powers']: ten_powers_list = [10, 10, 100, 100] random.shuffle(ten_powers_list) ten_power_factor1 = ten_powers_list.pop() ten_power_factor2 = ten_powers_list.pop() factors_list = [j + 1 for j in range(10)] random.shuffle(factors_list) self.objct = Fraction(('+', root * factors_list.pop() * ten_power_factor1, root * factors_list.pop() * ten_power_factor2)) # 2d & 3d OPTIONS # Fractions Products | Quotients elif q_kind in ['fractions_product', 'fractions_quotient']: # In some cases, the fractions will be generated # totally randomly if random.random() < 0: lil_box = [n + 2 for n in range(18)] a = random.choices( lil_box, weights=FRACTION_PRODUCT_AND_QUOTIENT_TABLE)[0] b = random.choices( lil_box, weights=FRACTION_PRODUCT_AND_QUOTIENT_TABLE)[0] lil_box = [n + 2 for n in range(18)] c = random.choices( lil_box, weights=FRACTION_PRODUCT_AND_QUOTIENT_TABLE)[0] d = random.choices( lil_box, weights=FRACTION_PRODUCT_AND_QUOTIENT_TABLE)[0] f1 = Fraction((random.choices(['+', '-'], cum_weights=[0.75, 1])[0], Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], a)), Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], b)))) f2 = Fraction((random.choices(['+', '-'], cum_weights=[0.75, 1])[0], Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], c)), Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], d)))) # f1 = f1.simplified() # f2 = f2.simplified() # In all other cases (80%), we'll define a "seed" a plus two # randomly numbers i and j to form the Product | Quotient: # a×i / b × c / a × j # Where b is a randomly number coprime to a×i # and c is a randomly number coprime to a×j else: a = random.randint(2, 8) lil_box = [i + 2 for i in range(7)] random.shuffle(lil_box) i = lil_box.pop() j = lil_box.pop() b = random.choice(coprimes_to(a * i, [n + 2 for n in range(15)])) c = random.choice(not_coprimes_to(b, [n + 2 for n in range(30)], exclude=[a * j])) f1 = Fraction((random.choices(['+', '-'], cum_weights=[0.75, 1])[0], Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], a * i)), Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], b)))) f2 = Fraction((random.choices(['+', '-'], cum_weights=[0.75, 1])[0], Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], c)), Item((random.choices(['+', '-'], cum_weights=[0.80, 1])[0], a * j)))) if random.choice([True, False]): f3 = f1.clone() f1 = f2.clone() f2 = f3.clone() if q_kind == 'fractions_quotient': f2 = f2.invert() if q_kind == 'fractions_product': self.objct = Product([f1, f2]) elif q_kind == 'fractions_quotient': self.objct = Quotient(('+', f1, f2, 1, 'use_divide_symbol')) # 4th OPTION # Fractions Sums elif q_kind == 'fractions_sum': randomly_position = random\ .choices([n for n in range(17)], weights=FRACTIONS_SUMS_SCALE_TABLE)[0] chosen_seed_and_generator = FRACTIONS_SUMS_TABLE[randomly_position] seed = random.randint(2, chosen_seed_and_generator[1]) # The following test is only intended to avoid having "high" # results too often. We just check if the common denominator # will be higher than 75 (arbitrary) and if yes, we redetermine # it once. We don't do it twice since we don't want to totally # forbid high denominators. if seed * chosen_seed_and_generator[0][0] \ * chosen_seed_and_generator[0][1] >= 75: # __ seed = random.randint(2, chosen_seed_and_generator[1]) lil_box = [0, 1] gen1 = chosen_seed_and_generator[0][lil_box.pop()] gen2 = chosen_seed_and_generator[0][lil_box.pop()] den1 = Item(gen1 * seed) den2 = Item(gen2 * seed) temp1 = random.randint(1, 20) temp2 = random.randint(1, 20) num1 = Item(temp1 // gcd(temp1, gen1 * seed)) num2 = Item(temp2 // gcd(temp2, gen2 * seed)) f1 = Fraction((random.choices(['+', '-'], cum_weights=[0.7, 1])[0], num1, den1)) f2 = Fraction((random.choices(['+', '-'], cum_weights=[0.7, 1])[0], num2, den2)) self.objct = Sum([f1.simplified(), f2.simplified()]) # 5th # still to imagine:o) # Creation of the expression: number = 0 if 'expression_number' in options \ and is_natural(options['expression_number']): # __ number = options['expression_number'] self.expression = Expression(number, self.objct) # -------------------------------------------------------------------------- ## # @brief Returns the text of the question as a str
[docs] def text_to_str(self): M = shared.machine return M.write_math_style1(M.type_string(self.expression))
# -------------------------------------------------------------------------- ## # @brief Returns the answer of the question as a str
[docs] def answer_to_str(self): M = shared.machine result = "" while self.objct is not None: result += M.write_math_style1(M.type_string(self.expression)) self.objct = self.objct.calculate_next_step() if self.objct is not None: self.expression = Expression(self.expression.name, self.objct) return result