Source code for mathmaker.lib.document.content.calculation.order_of_operations

# -*- 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 decimal import Decimal

from mathmakerlib.calculus import is_integer, move_fracdigits_to
from mathmakerlib.calculus import remove_fracdigits_from, fix_fracdigits
from mathmakerlib.calculus import Number

from mathmaker.lib import shared
from mathmaker.lib.constants.latex import COLORED_QUESTION_MARK
from mathmaker.lib.core.base_calculus import (Item, Sum, Product, Division,
                                              Expandable, Value)
from mathmaker.lib.core.calculus import Expression
from mathmaker.lib.document.content import component

# Possible variants are identified with a number:
# 0: a + b×c            # 4: a×b + c
# 1: a + b÷c            # 5: a÷b + c
# 2: a - b×c            # 6: a×b - c
# 3: a - b÷c            # 7: a÷b - c

# 8: a×b + c×d          # 16: a + b×c + d
# 9: a×b - c×d          # 17: a + b÷c + d
# 10: a÷b + c×d         # 18: a - b×c + d
# 11: a÷b - c×d         # 19: a + b×c - d
# 12: a×b + c÷d         # 20: a - b×c - d
# 13: a×b - c÷d         # 21: a - b÷c + d
# 14: a÷b + c÷d         # 22: a + b÷c - d
# 15: a÷b - c÷d         # 23: a - b÷c - d

# 100: (a + b)×c            # 104: (a - b)×c
# 101: (a + b)÷c            # 105: (a - b)÷c
# 102: a×(b + c)            # 106: a×(b - c)
# 103: a÷(b + c)            # 107: a÷(b - c)

# 108: a×(b + c)×d
# 109: a×(b + c)÷d
# 110: a÷(b + c)×d
# 111: a÷(b + c)÷d
# 112: a×(b - c)×d
# 113: a×(b - c)÷d
# 114: a÷(b - c)×d
# 115: a÷(b - c)÷d

# 116: a×(b + c×d)          # 120: a×(b - c×d)
# 117: (b + c×d)×a          # 121: (b - c×d)×a
# 118: a×(c×d + b)          # 122: a×(c×d - b)
# 119: (c×d + b)×a          # 123: (c×d - b)×a

# 124: a×(b + c÷d)          # 128: a×(b - c÷d)
# 125: (b + c÷d)×a          # 129: (b - c÷d)×a
# 126: a×(c÷d + b)          # 130: a×(c÷d - b)
# 127: (c÷d + b)×a          # 131: (c÷d - b)×a

# 132: a÷(b + c×d)
# 133: a÷(c×d + b)

# 134: a÷(b - c×d)
# 135: a÷(c×d - b)

# 136: (a×b + c)÷d
# 137: (c + a×b)÷d

# 138: (a×b - c)÷d
# 139: (c - a×b)÷d

# 140: a÷(b + c÷d)
# 141: a÷(c÷d + b)
# 142: a÷(b - c÷d)
# 143: a÷(c÷d - b)

# 144: (a÷b + c)÷d
# 145: (c + a÷b)÷d
# 146: (a÷b -c)÷d
# 147: (c - a÷b)÷d

# 148: (a + b)×(c + d)          # 152: (a - b)×(c + d)
# 149: (a + b)÷(c + d)          # 153: (a - b)÷(c + d)
# 150: (a + b)×(c - d)          # 154: (a - b)×(c - d)
# 151: (a + b)÷(c - d)          # 155: (a - b)÷(c - d)

# 156: a + b×(c + d)            # 160: a - b×(c + d)
# 157: a + b÷(c + d)            # 161: a - b÷(c + d)
# 158: a + b×(c - d)            # 162: a - b×(c - d)
# 159: a + b÷(c - d)            # 163: a - b÷(c - d)

# 164: a×(b + c) + d            # 168: a×(b - c) + d
# 165: a×(b + c) - d            # 169: a×(b - c) - d
# 166: a÷(b + c) + d            # 170: a÷(b - c) + d
# 167: a÷(b + c) - d            # 171: a÷(b - c) - d

# 172: (a + b)×c + d            # 176: (a - b)×c + d
# 173: (a + b)×c - d            # 177: (a - b)×c - d
# 174: (a + b)÷c + d            # 178: (a - b)÷c + d
# 175: (a + b)÷c - d            # 179: (a - b)÷c - d
# 180: a + (b + c)×d            # 184: a - (b + c)×d
# 181: a + (b + c)÷d            # 185: a - (b + c)÷d
# 182: a + (b - c)×d            # 186: a - (b - c)×d
# 183: a + (b - c)÷d            # 187: a - (b - c)÷d


[docs]class sub_object(component.structure):
[docs] def adjust_nb_for_variant_11(self, a, b, c, d): """ Reorder the 4 numbers to ensure a÷b - c×d >= 0 May (recursively if needed) change some values by multiplying them by 10 (if there's no other solution). """ if a >= c * d: return (a, b, c, d) if b >= c * d: return (b, a, c, d) if c >= a * b: return (c, d, a, b) if d >= a * b: return (d, c, a, b) if 10 * a >= c * d: return (a * 10, b, c, d) if 10 * b >= c * d: return (b * 10, a, c, d) if 10 * c >= a * b: return (c * 10, d, a, b) if 10 * d >= a * b: return (d * 10, c, a, b) # No solution has been found, we'll recursively test with a * 10 # (what will actually lead to test a * 100, then if necessary # a * 1000 etc. but that shouldn't go too far with the intended # numbers' range) return self.adjust_nb_for_variant_11(10 * max(a, b), min(a, b), c, d)
[docs] def adjust_nb_for_variant_13(self, a, b, c, d): """ Reorder the 4 numbers to ensure a×b - c÷d >= 0 May (recursively if needed) change some values by multiplying them by 10 (if there's no other solution). """ if a * b >= c: return (a, b, c, d) elif a * b >= d: return (a, b, d, c) else: # Necessarily, c * d >= a because a, b, c, d are all positive return (c, d, a, b)
[docs] def adjust_depth(self, depth, n=None, **kwargs): """ Return depth to use to split a number, depending on variant etc. This is to ensure a correct minimal value. :param depth: current depth (for instance, the default one, or the one given by the user) :type depth: int :param n: the number to split :type n: a number (int or Decimal) :rtype: int """ if Number(n).is_power_of_10() and depth == 0: depth = 1 # mad stands for maximum added depth if self.nb_variant.startswith('decimal'): mad = int(self.nb_variant[-1]) - Number(n).fracdigits_nb() else: mad = 0 mad = mad if mad > 0 else 0 if self.variant in [100, 102, 104, 106]: # (a + b)×c a×(b + c) (a - b)×c a×(b - c) return depth + random.choice([i for i in range(mad + 1)]) elif self.variant in [101, 105]: # (a ± b)÷c if (not self.allow_division_by_decimal and self.nb_variant == 'decimal1' and is_integer(n)): return max(depth, Number(n).fracdigits_nb() + 1) elif self.variant in [103, 107]: # a÷(b + c) a÷(b - c) N = kwargs['N'] return max(depth, mad - Number(N).fracdigits_nb(), random.choice([i for i in range(mad + 1)])) elif self.variant in [108, 112, 109, 113, 110, 114, 111, 115]: # a×(b ± c)×d a×(b ± c)÷d a÷(b ± c)×d a÷(b ± c)÷d N, P = kwargs['N'], kwargs['P'] return max(depth, mad - Number(N).fracdigits_nb() - Number(P).fracdigits_nb(), random.choice([i for i in range(mad + 1)])) elif 148 <= self.variant <= 155: # (a±b)×(c±d) and (a±b)÷(c±d) last = kwargs.get('last', False) if (self.nb_variant.startswith('decimal') and is_integer(n)): if (n <= 1 or n <= 6 or (7 <= n <= 20 and random.choice([True, True, False])) or (last and is_integer(kwargs['N']) and is_integer(kwargs['P']))): return max(depth, int(self.nb_variant[-1])) else: return depth + random.choice([i for i in range(mad + 1)]) else: return depth + random.choice([i for i in range(mad + 1)]) elif 156 <= self.variant <= 187: # a ± b×(c ± d) and a ± b÷(c ± d) and symmetrics # (a ± b)×c ± d; (a ± b)÷c ± d # and their symmetrics d ± (a ± b)×c; d ± (a ± b)÷c if (self.nb_variant.startswith('decimal') and is_integer(n) and ((is_integer(kwargs['N']) and is_integer(kwargs['P'])) or n <= 6 or (7 <= n <= 20 and random.choice([True, True, False])))): return max(depth, int(self.nb_variant[-1])) else: return depth + random.choice([i for i in range(mad + 1)]) return depth
[docs] def adjust_numbers(self): # As the pairs for products and quotients should be shuffled, but as # the pairs can be either (self.nb1; self.nb2) or (self.nb2; self.nb3) # etc. depending on the exact variant, we have to do it here. if random.choice([True, False]): if (0 <= self.variant <= 3) or (16 <= self.variant <= 20): self.nb2, self.nb3 = self.nb3, self.nb2 elif 4 <= self.variant <= 7: self.nb1, self.nb2 = self.nb2, self.nb1 elif (100 <= self.variant <= 107) or (148 <= self.variant <= 155): self.nb1, self.nb2 = self.nb2, self.nb1 elif (108 <= self.variant <= 115) or (156 <= self.variant <= 187): self.nb2, self.nb3 = self.nb3, self.nb2 elif 116 <= self.variant <= 147: self.nb3, self.nb4 = self.nb4, self.nb3 if 8 <= self.variant <= 15: if random.choice([True, False]): self.nb1, self.nb2 = self.nb2, self.nb1 if random.choice([True, False]): self.nb3, self.nb4 = self.nb4, self.nb3 if 116 <= self.variant <= 123: # In these cases, when self.nb2 is not integer, it gives quite # often "big" calculation with small numbers already. So most of # the time we try to avoid this. if (self.nb_variant.startswith('decimal') and not is_integer(self.nb2) and random.choice([True, True, True, False])): try: self.nb2, self.nb1, self.nb3, self.nb4 =\ remove_fracdigits_from(self.nb2, to=[self.nb1, self.nb3, self.nb4]) except ValueError: pass if not self.allow_division_by_decimal: if self.variant in [5, 7, 10, 11, 14, 15, ]: if not is_integer(self.nb2): if is_integer(self.nb1): self.nb1, self.nb2 = self.nb2, self.nb1 else: self.nb2, self.nb1 = fix_fracdigits(self.nb2, self.nb1) if self.variant in [1, 3, 17, 21, 22, 23]: if not is_integer(self.nb3): if is_integer(self.nb2): self.nb2, self.nb3 = self.nb3, self.nb2 else: self.nb3, self.nb2 = fix_fracdigits(self.nb3, self.nb2) if self.variant in [12, 13, 14, 15, ]: if not is_integer(self.nb4): self.nb3, self.nb4 = self.nb4, self.nb3 if self.variant in [101, 103, 105, 107, ]: if not is_integer(self.nb2): if self.nb_variant == 'decimal1': self.nb1, self.nb2 = self.nb2, self.nb1 else: self.nb1, self.nb2 = \ move_fracdigits_to(self.nb1, from_nb=[self.nb2]) if self.variant in [109, 110, 113, 114]: if not is_integer(self.nb3): if self.nb_variant == 'decimal1': self.nb2, self.nb3 = self.nb3, self.nb2 else: self.nb2, self.nb3 = \ move_fracdigits_to(self.nb2, from_nb=[self.nb3]) if self.variant in [111, 115]: self.nb1, self.nb2, self.nb3 = \ move_fracdigits_to(self.nb1, from_nb=[self.nb2, self.nb3]) if 132 <= self.variant <= 135: if not is_integer(self.nb2): if self.nb_variant == 'decimal1': self.nb1, self.nb2 = self.nb2, self.nb1 else: try: self.nb2, self.nb1 = \ remove_fracdigits_from(self.nb2, to=[self.nb1]) except ValueError: self.nb1 += random.choice([i for i in range(-4, 5) if i != 0]) self.nb2, self.nb1 = \ remove_fracdigits_from(self.nb2, to=[self.nb1]) if 124 <= self.variant <= 131 or 136 <= self.variant <= 139: if not is_integer(self.nb4): if self.nb_variant == 'decimal1': self.nb3, self.nb4 = self.nb4, self.nb3 else: try: self.nb4, self.nb3 = \ remove_fracdigits_from(self.nb4, to=[self.nb3]) except ValueError: self.nb3 += random.choice([i for i in range(-4, 5) if i != 0]) self.nb4, self.nb3 = \ remove_fracdigits_from(self.nb4, to=[self.nb3]) if 140 <= self.variant <= 147: rnd = random.choice([i for i in range(-4, 5) if i != 0]) if not is_integer(self.nb2): try: self.nb2, self.nb1, self.nb3 = remove_fracdigits_from( self.nb2, to=[self.nb1, self.nb3]) except ValueError: self.nb1 += rnd self.nb2, self.nb1 = remove_fracdigits_from( self.nb2, to=[self.nb1]) if not is_integer(self.nb4): try: self.nb4, self.nb3, self.nb1 = remove_fracdigits_from( self.nb4, to=[self.nb3, self.nb1]) except ValueError: self.nb3 += rnd self.nb4, self.nb3 = remove_fracdigits_from( self.nb4, to=[self.nb3]) if self.variant in [149, 151, 153, 155, 174, 175, 178, 179, 181, 183, 185, 187]: if not is_integer(self.nb2): if is_integer(self.nb1): self.nb1, self.nb2 = self.nb2, self.nb1 else: self.nb2, self.nb1 = remove_fracdigits_from( self.nb2, to=[self.nb1]) if self.variant in [157, 159, 161, 163, 166, 170, 167, 171]: if not is_integer(self.nb3): if is_integer(self.nb2): self.nb2, self.nb3 = self.nb3, self.nb2 else: self.nb3, self.nb1, self.nb2 = remove_fracdigits_from( self.nb3, to=[self.nb1, self.nb2]) if (self.variant in [14, 15] and self.nb_variant.startswith('decimal') and all(is_integer(x) for x in [self.nb1 * self.nb2, self.nb2, self.nb3 * self.nb4, self.nb4])): if not is_integer(self.nb1) and is_integer(self.nb1 * self.nb2): if not is_integer(self.nb3 * self.nb4 / 10): self.nb1, self.nb3 = fix_fracdigits(self.nb1, self.nb3) else: self.nb2 += random.choice([-1, 1]) if not is_integer(self.nb3) and is_integer(self.nb3 * self.nb4): if not is_integer(self.nb1 * self.nb2 / 10): self.nb3, self.nb1 = fix_fracdigits(self.nb3, self.nb1) else: self.nb4 += random.choice([-1, 1]) if self.variant in [17, 21, 22, 23]: if (self.nb_variant.startswith('decimal') and all(is_integer(x) for x in [self.nb1, self.nb2 * self.nb3, self.nb4])): if random.choice([True, False]): self.nb2, self.nb1, self.nb4 = fix_fracdigits(self.nb2, self.nb1, self.nb4) else: self.nb2, self.nb4, self.nb1 = fix_fracdigits(self.nb2, self.nb4, self.nb1)
def _create_0to23(self): a, b, c = self.nb1, self.nb2, self.nb3 if self.variant >= 8: d = self.nb4 # 11 and 13: a÷b - c×d and a×b - c÷d if self.variant == 11: if (self.nb_variant.startswith('decimal') and not is_integer(a) and all(is_integer(x) for x in [a * b, c, d])): a, c, d = fix_fracdigits(a, c, d) elif self.variant == 13: if (self.nb_variant.startswith('decimal') and not is_integer(c) and all(is_integer(x) for x in [a, b, c * d])): c, a, b = fix_fracdigits(c, a, b) if (self.subvariant == 'only_positive' and self.variant == 11 and a - c * d < 0 and self.nb_variant.startswith('decimal')): if not is_integer(a) and a * 10 - c * d / 10 >= 0: a, c, d = fix_fracdigits(a, c, d) else: self.variant = 13 a, b, c, d = c, d, a, b elif (self.subvariant == 'only_positive' and self.variant == 13 and a * b - c < 0 and self.nb_variant.startswith('decimal')): self.variant = 11 a, b, c, d = c, d, a, b if self.variant == 0: # a + b×c self.obj = Sum([a, Product([b, c])]) self.watch('no negative; decimals distribution; ' 'a isnt 0; b isnt 1; c isnt 1', a, b, c) elif self.variant == 1: # a + b÷c if (self.nb_variant.startswith('decimal') and all(is_integer(x) for x in [a, b * c, c])): b, a = fix_fracdigits(b, a) b = b * c self.obj = Sum([a, Division(('+', b, c))]) self.watch('no negative; decimals distribution; ' 'a isnt 0; c isnt 0; c isnt 1; c isnt deci', a, b, c) elif self.variant == 2: # a - b×c if self.subvariant == 'only_positive': if a < b * c: a += b * c self.obj = Sum([a, Product([-b, c])]) self.watch('no negative; decimals distribution; ' 'a isnt 0; b isnt 1; c isnt 1', a, b, c) elif self.variant == 3: # a - b÷c if (self.nb_variant.startswith('decimal') and all(is_integer(x) for x in [a, b * c, c])): b, a = fix_fracdigits(b, a) if self.subvariant == 'only_positive': if a < b: a += b b = b * c self.obj = Sum([a, Division(('-', b, c))]) self.watch('no negative; decimals distribution; ' 'a isnt 0; c isnt 0; c isnt 1; c isnt deci', a, b, c) elif self.variant == 4: # a×b + c self.obj = Sum([Product([a, b]), c]) self.watch('no negative; decimals distribution; ' 'c isnt 0; b isnt 1; a isnt 1', a, b, c) elif self.variant == 5: # a÷b + c if (self.nb_variant.startswith('decimal') and all(is_integer(x) for x in [a * b, b, c])): a, c = fix_fracdigits(a, c) a = a * b self.obj = Sum([Division(('+', a, b)), c]) self.watch('no negative; decimals distribution; ' 'c isnt 0; b isnt 0; b isnt 1; b isnt deci', a, b, c) elif self.variant == 6: # a×b - c if self.subvariant == 'only_positive' and a * b < c: depth = Number(a * b).fracdigits_nb() c = Decimal(str(random.choice( [i + 1 for i in range(int(a * b * (10 ** depth)))]))) \ / Decimal((10 ** depth)) self.obj = Sum([Product([a, b]), -c]) self.watch('no negative; decimals distribution; ' 'c isnt 0; b isnt 1; a isnt 1', a, b, c) elif self.variant == 7: # a÷b - c if self.subvariant == 'only_positive' and a < c: if self.nb_variant.startswith('decimal'): depth = int(self.nb_variant[-1]) + self.allow_extra_digits else: depth = self.allow_extra_digits if self.nb_variant.startswith('decimal'): for i in range(depth): c = c / 10 if a >= c: break else: # no break: # We have divided c by 10 as much as allowed # and yet a < c, so no other choice than # randomly choose a new decimal value c = Decimal(str(random.choice( [i + 1 for i in range(int(min(a, b) * (10 ** depth)))] ))) / Decimal(str((10 ** depth))) else: # no choice but to randomly choose a new natural if random.choice([True, True, False]): a, b = max(a, b), min(a, b) c = Decimal(str( random.choice([i + 1 for i in range(int(a) - 1)]))) else: c = Decimal(str( random.choice([i + 1 for i in range( int(min(a, b)) - 1)]))) a = a * b self.obj = Sum([Division(('+', a, b)), -c]) self.watch('no negative; decimals distribution; ' 'c isnt 0; b isnt 0; b isnt 1; b isnt deci', a, b, c) elif self.variant == 8: # a×b + c×d self.obj = Sum([Product([a, b]), Product([c, d])]) self.watch('no negative; decimals distribution; ' 'a isnt 1; b isnt 1; c isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 9: # a×b - c×d if self.subvariant == 'only_positive' and a * b < c * d: a, b, c, d = c, d, a, b self.obj = Sum([Product([a, b]), Product([-c, d])]) self.watch('no negative; decimals distribution; ' 'a isnt 1; b isnt 1; c isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 10: # a÷b + c×d if (self.nb_variant.startswith('decimal') and all(is_integer(x) for x in [a * b, c, d])): a, c, d = fix_fracdigits(a, c, d) a = a * b self.obj = Sum([Division(('+', a, b)), Product([c, d])]) self.watch('no negative; decimals distribution; ' 'b isnt 1; c isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 11: # a÷b - c×d # Special case already managed at start of _create_0to23() if (self.subvariant == 'only_positive' and not self.nb_variant.startswith('decimal')): a, b, c, d = self.adjust_nb_for_variant_11(a, b, c, d) a = a * b self.obj = Sum([Division(('+', a, b)), Product([-c, d])]) e = a / b - c * d self.watch('no negative; decimals distribution; ' 'b isnt 1; c isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) self.watch('no negative', e, letters='e') elif self.variant == 12: # a×b + c÷d if (self.nb_variant.startswith('decimal') and all(is_integer(x) for x in [a, b, c * d])): c, a, b = fix_fracdigits(c, a, b) c = c * d self.obj = Sum([Product([a, b]), Division(('+', c, d))]) self.watch('no negative; decimals distribution; ' 'a isnt 1; b isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 13: # a×b - c÷d # Special case already managed at start of _create_0to23() if (self.subvariant == 'only_positive' and not self.nb_variant.startswith('decimal')): a, b, c, d = self.adjust_nb_for_variant_13(a, b, c, d) c = c * d self.obj = Sum([Product([a, b]), Division(('-', c, d))]) self.watch('no negative; decimals distribution; ' 'a isnt 1; b isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) e = a * b - c / d self.watch('no negative', e, letters='e') elif self.variant == 14: # a÷b + c÷d a, c = a * b, c * d self.obj = Sum([Division(('+', a, b)), Division(('+', c, d))]) self.watch('no negative; decimals distribution; ' 'b isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 15: # a÷b - c÷d if self.subvariant == 'only_positive' and a < c: a, b, c, d = c, d, a, b a, c = a * b, c * d self.obj = Sum([Division(('+', a, b)), Division(('-', c, d))]) self.watch('no negative; decimals distribution; ' 'b isnt 1; d isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 16: # a + b×c + d self.obj = Sum([a, Product([b, c]), d]) self.watch('no negative; decimals distribution; ' 'b isnt 1; c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 17: # a + b÷c + d b = b * c self.obj = Sum([a, Division(('+', b, c)), d]) self.watch('no negative; decimals distribution; ' 'c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 18: # a - b×c + d if self.subvariant == 'only_positive': if a < b * c: a += b * c self.obj = Sum([a, Product([-b, c]), d]) self.watch('no negative; decimals distribution; ' 'b isnt 1; c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 19: # a + b×c - d if self.subvariant == 'only_positive': if a + b * c < d: a = a + d self.obj = Sum([a, Product([b, c]), -d]) self.watch('no negative; decimals distribution; ' 'b isnt 1; c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 20: # a - b×c - d if self.subvariant == 'only_positive': if d + b * c > a: a = a + d + b * c self.obj = Sum([a, Product([-b, c]), -d]) self.watch('no negative; decimals distribution; ' 'b isnt 1; c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 21: # a - b÷c + d if self.subvariant == 'only_positive': if a < b: a += b b = b * c self.obj = Sum([a, Division(('-', b, c)), d]) self.watch('no negative; decimals distribution; ' 'c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 22: # a + b÷c - d if self.subvariant == 'only_positive': if a + b < d: a += d b = b * c self.obj = Sum([a, Division(('+', b, c)), -d]) self.watch('no negative; decimals distribution; ' 'c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) elif self.variant == 23: # a - b÷c - d if self.subvariant == 'only_positive': if a < b: a += b if a < b + d: a += d b = b * c self.obj = Sum([a, Division(('-', b, c)), -d]) self.watch('no negative; decimals distribution; ' 'c isnt 1' 'a isnt 0; b isnt 0; c isnt 0; d isnt 0', a, b, c, d) abcd = [a, b, c] if self.variant >= 8: abcd.append(d) return abcd def _create_100_104(self): # (a + b)×c (a - b)×c ops = '+' if self.variant == 100 else '-' opn = 1 if self.variant == 100 else -1 c = self.nb2 a, b = Number(self.nb1) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb1)) self.obj = Product([Sum([Item(a), Item(opn * b)]), Item(c)]) self.watch('no negative; c isnt 1; decimals distribution', a, b, c) return [a, b, c] def _create_101_105(self): # (a + b)÷c (a - b)÷c ops = '+' if self.variant == 101 else '-' opn = 1 if self.variant == 101 else -1 c = self.nb2 self.nb1 = self.nb1 * self.nb2 a, b = Number(self.nb1) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb1)) self.obj = Division(('+', Sum([a, opn * b]), c)) self.watch('no negative; c isnt 1; c isnt deci; decimals distribution', a, b, c) return [a, b, c] def _create_102_106(self): # a×(b + c) a×(b - c) ops = '+' if self.variant == 101 else '-' opn = 1 if self.variant == 101 else -1 a = self.nb1 b, c = Number(self.nb2) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb2)) self.obj = Product([Item(a), Sum([Item(b), Item(opn * c)])], compact_display=False) self.watch('no negative; a isnt 1; decimals distribution', a, b, c) return [a, b, c] def _create_103_107(self): # a÷(b + c) a÷(b - c) ops = '+' if self.variant == 101 else '-' opn = 1 if self.variant == 101 else -1 a = self.nb1 * self.nb2 b, c = Number(self.nb2) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb2, N=a)) self.obj = Division(('+', a, Sum([b, opn * c]))) d = b + opn * c self.watch('no negative; d isnt deci; decimals distribution', a, b, c, d) return [a, b, c] def _create_108_112(self): # a×(b ± c)×d ops = '+' if self.variant == 108 else '-' opn = 1 if self.variant == 108 else -1 a = self.nb1 d = self.nb3 b, c = Number(self.nb2) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb2, N=a, P=d)) self.obj = Product([Item(a), Sum([Item(b), Item(opn * c)]), Item(d)], compact_display=False) self.watch('no negative; a isnt 1; d isnt 1; decimals distribution', a, b, c, d) return [a, b, c, d] def _create_109_113(self): # a×(b ± c)÷d ops = '+' if self.variant == 109 else '-' opn = 1 if self.variant == 109 else -1 a = self.nb1 d = self.nb3 nb2 = self.nb2 self.nb2 = self.nb2 * self.nb3 b, c = Number(self.nb2) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb2, N=a, P=d)) if (all(is_integer(x) for x in [self.nb2, d, nb2]) or (not is_integer(self.nb2) and not a % 10 == 0)): self.obj = Product([a, Division(('+', Sum([b, opn * c]), d))], compact_display=False) else: self.obj = Division(('+', Product([a, Sum([b, opn * c])], compact_display=False), d)) self.watch('no negative; a isnt 1; d isnt 1; d isnt deci; ' 'decimals distribution', a, b, c, d) return [a, b, c, d] def _create_110_114(self): # a÷(b ± c)×d ops = '+' if self.variant == 110 else '-' opn = 1 if self.variant == 110 else -1 a = self.nb2 * self.nb3 d = self.nb1 b, c = Number(self.nb3) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb3, N=a, P=d)) self.obj = Product([Division(('+', a, Sum([b, opn * c]))), d], compact_display=False) e = self.nb3 self.watch('no negative; d isnt 1; e isnt deci; ' 'decimals distribution', a, b, c, d, e) return [a, b, c, d] def _create_111_115(self): # a÷(b ± c)÷d ops = '+' if self.variant == 111 else '-' opn = 1 if self.variant == 111 else -1 a = self.nb1 * self.nb2 * self.nb3 d = self.nb3 b, c = Number(self.nb2) \ .split(operation=ops, dig=self.adjust_depth(self.allow_extra_digits, n=self.nb2, N=a, P=d)) self.obj = Division(('+', Division(('+', a, Sum([b, opn * c]))), d)) e = self.nb2 self.watch('no negative; d isnt 1; d isnt deci; ' 'e isnt deci; decimals distribution', a, b, c, d, e) return [a, b, c, d] def _create_116to123(self): # 116: a×(b + c×d) 117: (b + c×d)×a # 118: a×(c×d + b) 119: (c×d + b)×a (124) # 120: a×(b - c×d) 121: (b - c×d)×a # 122: a×(c×d - b) 123: (c×d - b)×a ((128)) symmetrics = {120: 122, 122: 120, 121: 123, 123: 121} opn = 1 if 116 <= self.variant <= 119 else -1 a, b, c, d = self.nb1, self.nb2, self.nb3, self.nb4 if 116 <= self.variant <= 119: if ((not self.subvariant == 'only_positive') or (self.subvariant == 'only_positive' and b - c * d > 0)): b = b - c * d if self.variant in [122, 123]: if self.subvariant == 'only_positive' and c * d - b < 0: self.variant = symmetrics[self.variant] elif c * d - b != 0: b = c * d - b if self.variant in [120, 121]: b = b + c * d if self.variant in [116, 120]: self.obj = Product([a, Sum([b, Product([opn * c, d])])], compact_display=False) elif self.variant in [117, 121]: self.obj = Product([Sum([b, Product([opn * c, d], compact_display=False)]), a], compact_display=False) elif self.variant in [118, 122]: self.obj = Product([a, Sum([Product([c, d], compact_display=False), opn * b])], compact_display=False) elif self.variant in [119, 123]: self.obj = Product([Sum([Product([c, d], compact_display=False), opn * b]), a], compact_display=False) self.watch('no negative; decimals distribution; a isnt 1; ' 'c isnt 1; d isnt 1; b isnt 0', a, b, c, d) return [a, b, c, d] def _create_124to131(self): # 124: a×(b + c÷d) 125: (b + c÷d)×a # 126: a×(c÷d + b) 127: (c÷d + b)×a # 128: a×(b - c÷d) 129: (b - c÷d)×a # 130: a×(c÷d - b) 131: (c÷d - b)×a # We won't deal with only integers problems because they cannot show up # For instance if c÷d is 4÷5, then b being initially an integer, will # become decimal after addition or subtraction of c÷d symmetrics = {128: 130, 130: 128, 129: 131, 131: 129} ops = '+' if 124 <= self.variant <= 127 else '-' opn = 1 if 124 <= self.variant <= 127 else -1 a, b, c, d = self.nb1, self.nb2, self.nb3, self.nb4 if 124 <= self.variant <= 127: if ((not self.subvariant == 'only_positive') or (self.subvariant == 'only_positive' and b - c > 0)): b = b - c elif self.variant in [130, 131]: if ((not self.subvariant == 'only_positive') or (self.subvariant == 'only_positive' and c - b > 0)): b = c - b elif self.subvariant == 'only_positive': # Here we have c - b <= 0 self.variant = symmetrics[self.variant] if self.variant in [128, 129]: b = b + c c = c * d if self.variant in [124, 128]: self.obj = Product([a, Sum([b, Division((ops, c, d))])], compact_display=False) elif self.variant in [125, 129]: self.obj = Product([Sum([b, Division((ops, c, d))]), a], compact_display=False) elif self.variant in [126, 130]: self.obj = Product([a, Sum([Division(('+', c, d)), opn * b])], compact_display=False) elif self.variant in [127, 131]: self.obj = Product([Sum([Division(('+', c, d)), opn * b]), a], compact_display=False) # a×(b ± c÷d) and variants self.watch('no negative; decimals distribution; a isnt 1; d isnt 1; ' 'd isnt deci; b isnt 0', a, b, c, d) return [a, b, c, d] def _create_132_133(self): # a÷(b + c×d) a÷(c×d + b) a, b, c, d = self.nb1, self.nb2, self.nb3, self.nb4 if (self.nb_variant.startswith('decimal') and not is_integer(a) and all(is_integer(x) for x in [b, c, d, a * (b + c * d)])): try: a, b, c, d = remove_fracdigits_from(a, to=[b, c, d]) except ValueError: rnd = random.choice([i for i in range(-4, 5) if i != 0]) choice = random.choice([1, 2, 3]) if choice is 1: b += rnd elif choice is 2: c += rnd else: d += rnd a, b, c, d = remove_fracdigits_from(a, to=[b, c, d]) if (not self.allow_division_by_decimal and self.nb_variant.startswith('decimal') and not is_integer(b + c * d)): if not is_integer(b): # For instance, b + c×d is 0.8 + 10×6 try: b, c, d = remove_fracdigits_from(b, to=[c, d]) # Now it is 8 + 10×0.6 except ValueError: # Bad luck, it was something like 0.8 + 50×10 # (and a = 20). We have to change c or d in order to # ensure one of them at least can be turned into a # decimal rnd = random.choice([i for i in range(-4, 5) if i != 0]) if random.choice([True, False]): c += rnd else: d += rnd b, c, d = remove_fracdigits_from(b, to=[c, d]) # Now it's sure b is an integer # this doesn't mean that b + c*d is if not is_integer(b + c * d): if ((not self.subvariant == 'only_positive') or (self.subvariant == 'only_positive' and b - c * d > 0)): b = b - c * d else: x = c * d y = random.choice([n for n in range(int(b) + 1)]) b = Decimal(y) + 1 - (x - int(x)) a = self.nb1 * (b + c * d) if self.variant == 132: self.obj = Division(('+', a, Sum([b, Product([c, d])]))) elif self.variant == 133: self.obj = Division(('+', a, Sum([Product([c, d]), b]))) # a÷(b + c×d) e = b + c * d self.watch('no negative; decimals distribution; c isnt 1; d isnt 1; ' 'e isnt deci; e isnt 0; b isnt 0', a, b, c, d, e) return [a, b, c, d] def _create_134_135(self): # a÷(b - c×d) a÷(c×d - b) a, b, c, d = self.nb1, self.nb2, self.nb3, self.nb4 if (self.nb_variant.startswith('decimal') and not is_integer(a) and all(is_integer(x) for x in [b, c, d, a * b])): try: a, c, d = remove_fracdigits_from(a, to=[c, d]) except ValueError: rnd = random.choice([i for i in range(-4, 5) if i != 0]) if random.choice([True, False]): c += rnd else: d += rnd a, c, d = remove_fracdigits_from(a, to=[c, d]) if (not self.allow_division_by_decimal and self.nb_variant.startswith('decimal') and not is_integer(b)): try: b, c, d = remove_fracdigits_from(b, to=[c, d]) except ValueError: rnd = random.choice([i for i in range(-4, 5) if i != 0]) if random.choice([True, False]): c += rnd else: d += rnd b, c, d = remove_fracdigits_from(b, to=[c, d]) if (self.variant == 135 and self.subvariant == 'only_positive' and c * d - b <= 0): self.variant = 134 if self.variant == 134: b = b + c * d elif self.variant == 135: b = c * d - b a = self.nb1 * abs(b - c * d) if self.variant == 134: self.obj = Division(('+', a, Sum([b, Product([-c, d])]))) e = b - c * d elif self.variant == 135: self.obj = Division(('+', a, Sum([Product([c, d]), -b]))) e = c * d - b # a÷(b - c×d) self.watch('no negative; decimals distribution; c isnt 1; d isnt 1; ' 'e isnt deci; b isnt 0; a isnt 0', a, b, c, d, e) return [a, b, c, d] def _create_136_137(self): # (a×b + c)÷d (c + a×b)÷d a, b, c, d = self.nb1, self.nb2, self.nb3, self.nb4 if (self.nb_variant.startswith('decimal') and is_integer(c * d) and not is_integer(c)): if (a * b >= c * d or (a * b < c * d and self.subvariant != 'only_positive')): # if a*b == c*d then it's still ok to swap them # (this is in order to make the decimal visible) # and if a and b are decimals to (e.g. subvariant is # decimal2 or more), it doesn't hurt neither a, b, c, d = c, d, a, b else: # subvariant is only_positive # and it is not possible to swap a, b and c, d # (in order to move the decimal to the product a×b) try: c, a, b = remove_fracdigits_from(c, to=[a, b]) except ValueError: rnd = random.choice([i for i in range(-4, 5) if i != 0]) if random.choice([True, False]): a += rnd else: b += rnd c, a, b = remove_fracdigits_from(c, to=[a, b]) else: if (a * b > c * d and self.subvariant == 'only_positive'): if (all(is_integer(x) for x in [a, b]) or not is_integer(a * b)): a, b, c, d = c, d, a, b else: if not is_integer(b): a, b = b, a # Now, the decimal is in a, for sure try: a, c = remove_fracdigits_from(a, to=[c]) except ValueError: c += random.choice([i + 1 for i in range(9)]) a, c = remove_fracdigits_from(a, to=[c]) a, b, c, d = c, d, a, b if not is_integer(d): c, d = d, c if a * b == c * d: c = c * d else: c = c * d - a * b if self.variant == 136: self.obj = Division(('+', Sum([Product([a, b], compact_display=False), c]), d)) elif self.variant == 137: self.obj = Division(('+', Sum([c, Product([a, b], compact_display=False)]), d)) # (a×b + c)÷d (c + a×b)÷d self.watch('no negative; decimals distribution; d isnt 1; ' 'd isnt deci; a isnt 1; b isnt 1; c isnt 0', a, b, c, d) return [a, b, c, d] def _create_138_139(self): # (a×b - c)÷d (c - a×b)÷d symmetrics = {138: 139, 139: 138} a, b, c, d = self.nb1, self.nb2, self.nb3, self.nb4 if (self.nb_variant == 'decimal1' and is_integer(c * d) and not is_integer(c)): # It is enough to swap a,b and c,d in all cases. # If this would lead to a negative number, then it is possible # to create the symmetric expression (i.e. 139) a, b, c, d = c, d, a, b c = c * d if self.subvariant == 'only_positive': if ((self.variant == 138 and a * b - c < 0) or (self.variant == 139 and a * b - c > 0)): self.variant = symmetrics[self.variant] if self.variant == 138: if a * b != c: c = a * b - c first_factor = Sum([Product([a, b], compact_display=False), -c]) self.obj = Division(('+', first_factor, d)) elif self.variant == 139: if a * b != c: c = a * b + c first_factor = Sum([c, Product([-a, b], compact_display=False)]) self.obj = Division(('+', first_factor, d)) # (a×b - c)÷d (c - a×b)÷d self.watch('no negative; decimals distribution; d isnt deci; c isnt 0', a, b, c, d) return [a, b, c, d] def _create_140to147(self): # a ÷ (b + c÷d) a ÷ (c÷d + b) # a ÷ (b - c÷d) a ÷ (c÷d - b) # (a÷b + c)÷d (c + a÷b)÷d # (a÷b - c)÷d (c - a÷b)÷d a, b, c, d = self.nb1, self.nb2, self.nb3, self.nb4 ops = '+' if self.variant in [140, 141, 144, 145] else '-' opn = 1 if self.variant in [140, 141, 144, 145] else -1 if (self.nb_variant == 'decimal1' and is_integer(c * d) and not is_integer(c)): if not is_integer(a * b / 10): try: c, a = remove_fracdigits_from(c, to=[a]) except ValueError: a += random.choice([i for i in range(-4, 5) if i != 0]) c, a = remove_fracdigits_from(c, to=[a]) else: d += random.choice([-1, 1]) if d == 1: d = 3 elif (self.nb_variant == 'decimal1' and is_integer(a * b) and not is_integer(a)): if not is_integer(c * d / 10): try: a, c = remove_fracdigits_from(a, to=[c]) except ValueError: c += random.choice([i for i in range(-4, 5) if i != 0]) a, c = remove_fracdigits_from(a, to=[c]) else: b += random.choice([-1, 1]) if b == 1: b = 3 if 140 <= self.variant <= 143: if self.variant in [140, 141]: if b < c and self.subvariant == 'only_positive': a, b, c, d = d, c, b, a if (not self.allow_division_by_decimal and not is_integer(d)): try: d, c = remove_fracdigits_from(d, to=[c]) except ValueError: c += random.choice([i for i in range(-4, 5) if i != 0]) d, c = remove_fracdigits_from(d, to=[c]) a = a * b if c != b: b = b - c elif self.variant in [142, 143]: if self.variant == 142: if b < c and self.subvariant == 'only_positive': self.variant = 143 elif self.variant == 143: if c < b and self.subvariant == 'only_positive': self.variant = 142 a = a * b if b != c: if self.variant == 142: b = b + c else: b = c - b c = c * d else: if self.variant in [144, 145]: if a > c * d and self.subvariant == 'only_positive': a, b, c, d = c, d, a, b if c * d != a: c = c * d - a else: # Do not forget the case c * d == a: c = c * d elif self.variant in [146, 147]: if self.variant == 146: if a <= c * d and self.subvariant == 'only_positive': self.variant = 147 elif self.variant == 147: if c * d <= a and self.subvariant == 'only_positive': self.variant = 146 if self.variant == 146: c = a - c * d else: c = c * d + a a = a * b if self.variant in [140, 142]: self.obj = Division(('+', a, Sum([b, Division((ops, c, d))]))) elif self.variant in [141, 143]: self.obj = Division(('+', a, Sum([Division(('+', c, d)), opn * b]))) elif self.variant in [144, 146]: self.obj = Division(('+', Sum([Division(('+', a, b)), opn * c]), d)) elif self.variant in [145, 147]: self.obj = Division(('+', Sum([c, Division((ops, a, b))]), d)) # a ÷ (b ± c÷d) a ÷ (c÷d ± b) # (a÷b ± c)÷d (c ± a÷b)÷d watch_rules = 'no negative; decimals distribution; d isnt 1; ' \ + 'd isnt deci' if 140 <= self.variant <= 143: if self.variant in [140, 141]: e = b + c / d elif self.variant == 142: e = b - c / d elif self.variant == 143: e = c / d - b watch_rules += '; e isnt deci; e inst 0; b isnt 0' self.watch(watch_rules, a, b, c, d, e) # (a÷b + c)÷d else: watch_rules += '; b isnt 1; b isnt deci; c isnt 0' self.watch(watch_rules, a, b, c, d) return [a, b, c, d] def _create_148to155(self): # 148: (a + b)×(c + d) # 152: (a - b)×(c + d) # 149: (a + b)÷(c + d) # 153: (a - b)÷(c + d) # 150: (a + b)×(c - d) # 154: (a - b)×(c - d) # 151: (a + b)÷(c - d) # 155: (a - b)÷(c - d) ab_signs = dict.fromkeys([148, 149, 150, 151], '+') ab_signs.update(dict.fromkeys([152, 153, 154, 155], '-')) cd_signs = dict.fromkeys([148, 149, 152, 153], '+') cd_signs.update(dict.fromkeys([150, 151, 154, 155], '-')) opn_signs = {'+': 1, '-': -1} if self.variant in [148, 150, 152, 154]: a, b = Number(self.nb1) \ .split(operation=ab_signs[self.variant], dig=self.adjust_depth(self.allow_extra_digits, n=self.nb1)) else: a, b = Number(self.nb1 * self.nb2) \ .split(operation=ab_signs[self.variant], dig=self.adjust_depth(self.allow_extra_digits, n=self.nb1 * self.nb2)) c, d = Number(self.nb2) \ .split(operation=cd_signs[self.variant], dig=self.adjust_depth(self.allow_extra_digits, n=self.nb2, last=True, N=a, P=b)) nabs = opn_signs[ab_signs[self.variant]] ncds = opn_signs[cd_signs[self.variant]] if self.variant in [148, 150, 152, 154]: self.obj = Product([Sum([a, nabs * b]), Sum([c, ncds * d])], compact_display=False) else: self.obj = Division(('+', Sum([a, nabs * b]), Sum([c, ncds * d]))) # 148: (a + b)×(c + d) # 152: (a - b)×(c + d) # 149: (a + b)÷(c + d) # 153: (a - b)÷(c + d) # 150: (a + b)×(c - d) # 154: (a - b)×(c - d) # 151: (a + b)÷(c - d) # 155: (a - b)÷(c - d) e = c + ncds * d watch_rules = 'no negative; decimals distribution' if self.variant in [149, 151, 153, 155]: watch_rules += '; e isnt deci; e isnt 0' self.watch(watch_rules, a, b, c, d, e) return [a, b, c, d] def _create_156to171(self): # 156: a + b×(c + d) # 160: a - b×(c + d) # 157: a + b÷(c + d) # 161: a - b÷(c + d) # 158: a + b×(c - d) # 162: a - b×(c - d) # 159: a + b÷(c - d) # 163: a - b÷(c - d) # 164: a×(b + c) + d # 168: a×(b - c) + d # 165: a×(b + c) - d # 169: a×(b - c) - d # 166: a÷(b + c) + d # 170: a÷(b - c) + d # 167: a÷(b + c) - d # 171: a÷(b - c) - d symmetric = {156: 164, 164: 156, 157: 166, 166: 157, 158: 168, 168: 158, 159: 170, 170: 159, 160: 165, 165: 160, 161: 167, 167: 161, 162: 169, 169: 162, 163: 171, 171: 163} b_signs = dict.fromkeys([156, 157, 158, 159, 164, 166, 168, 170], '+') b_signs.update( dict.fromkeys([160, 161, 162, 163, 165, 167, 169, 171], '-')) cd_signs = dict.fromkeys([156, 157, 160, 161, 164, 166, 165, 167], '+') cd_signs.update( dict.fromkeys([158, 159, 162, 163, 168, 170, 169, 171], '-')) opn_signs = {'+': 1, '-': -1} nbs = opn_signs[b_signs[self.variant]] ncds = opn_signs[cd_signs[self.variant]] a, b = self.nb1, self.nb2 if self.subvariant == 'only_positive': if self.variant in [165, 169]: if b * self.nb3 - a < 0: self.variant = symmetric[self.variant] elif self.variant in [167, 171]: if b - a < 0: self.variant = symmetric[self.variant] if self.variant in [160, 162]: if a - b * self.nb3 < 0: a += b * self.nb3 elif self.variant in [161, 163]: if a - b < 0: a += b if self.variant in [157, 159, 161, 163, 166, 170, 167, 171]: b = self.nb2 * self.nb3 c, d = Number(self.nb3) \ .split(operation=cd_signs[self.variant], dig=self.adjust_depth(self.allow_extra_digits, n=self.nb3, N=a, P=b)) if self.variant in [156, 158, 160, 162]: self.obj = Sum([a, Product([nbs * b, Sum([c, ncds * d])], compact_display=False)]) elif self.variant in [164, 168, 165, 169]: self.obj = Sum([Product([b, Sum([c, ncds * d])], compact_display=False), nbs * a]) elif self.variant in [157, 159, 161, 163]: self.obj = Sum([a, Division((b_signs[self.variant], b, Sum([c, ncds * d])))]) elif self.variant in [166, 167, 170, 171]: self.obj = Sum([Division(('+', b, Sum([c, ncds * d]))), nbs * a]) # 156: a + b×(c + d) # 160: a - b×(c + d) # 157: a + b÷(c + d) # 161: a - b÷(c + d) # 158: a + b×(c - d) # 162: a - b×(c - d) # 159: a + b÷(c - d) # 163: a - b÷(c - d) # 164: a×(b + c) + d # 168: a×(b - c) + d # 165: a×(b + c) - d # 169: a×(b - c) - d # 166: a÷(b + c) + d # 170: a÷(b - c) + d # 167: a÷(b + c) - d # 171: a÷(b - c) - d watch_rules = 'no negative; decimals distribution' if self.variant in [156, 158, 160, 162, 164, 168, 165, 169]: watch_rules += '; b isnt 1' self.watch(watch_rules, a, b, c, d) if self.variant in [157, 159, 161, 163, 166, 170, 167, 171]: e = self.nb3 self.watch('e isnt deci', e, letters='e') if 160 <= self.variant <= 163 or self.variant in [165, 167, 169, 171]: if self.variant in [161, 163]: f = a - self.nb2 elif self.variant in [167, 171]: f = self.nb2 - a elif self.variant in [160, 162]: f = a - b * self.nb3 elif self.variant in [165, 169]: f = b * self.nb3 - a self.watch('no negative', f, letters='f') return [a, b, c, d] def _create_172to187(self): # (a ± b)×c ± d; (a ± b)÷c ± d # and their symmetrics d ± (a ± b)×c; d ± (a ± b)÷c symmetric = {172: 180, 180: 172, 173: 184, 184: 173, 174: 181, 181: 174, 175: 185, 185: 175, 176: 182, 182: 176, 177: 186, 186: 177, 178: 183, 183: 178, 179: 187, 187: 179} b_signs = dict.fromkeys([172, 173, 174, 175, 180, 181, 184, 185], '+') b_signs.update( dict.fromkeys([176, 177, 178, 179, 182, 183, 186, 187], '-')) d_signs = dict.fromkeys([172, 174, 176, 178, 180, 181, 182, 183], '+') d_signs.update( dict.fromkeys([173, 175, 177, 179, 184, 185, 186, 187], '-')) opn_signs = {'+': 1, '-': -1} nbs = opn_signs[b_signs[self.variant]] nds = opn_signs[d_signs[self.variant]] if (self.nb_variant.startswith('decimal') and self.variant in [174, 175, 178, 179, 181, 183, 185, 187] and not is_integer(self.nb1) and is_integer(self.nb1 * self.nb2)): try: remove_fracdigits_from(self.nb1, to=[self.nb3]) except ValueError: self.nb3 += random.choice([i for i in range(-4, 5) if i != 0]) remove_fracdigits_from(self.nb1, to=[self.nb3]) c, d = self.nb2, self.nb3 op = b_signs[self.variant] if self.variant in [172, 173, 176, 177, 180, 182, 184, 186]: nb_to_split = self.nb1 dig_level = self.adjust_depth(self.allow_extra_digits, n=self.nb1, N=c, P=d) else: nb_to_split = self.nb1 * self.nb2 dig_level = self.adjust_depth(self.allow_extra_digits, n=self.nb1 * self.nb2, N=c, P=d) a, b = Number(nb_to_split).split(operation=op, dig=dig_level) if self.subvariant == 'only_positive': if ((self.variant in [173, 177] and self.nb1 * self.nb2 < self.nb3) or (self.variant in [175, 179] and self.nb1 < self.nb3) or (self.variant in [184, 186] and self.nb1 * self.nb2 > self.nb3) or (self.variant in [185, 187] and self.nb1 > self.nb3)): self.variant = symmetric[self.variant] if self.variant in [172, 173, 176, 177]: self.obj = Sum([Product([Sum([a, nbs * b]), c], compact_display=False), nds * d]) elif self.variant in [180, 182, 184, 186]: self.obj = Sum([d, Product([Expandable((Item(nds), Sum([a, nbs * b]))), c], compact_display=False)]) elif self.variant in [174, 175, 178, 179]: self.obj = Sum([Division(('+', Sum([a, nbs * b]), c)), nds * d]) elif self.variant in [181, 183, 185, 187]: self.obj = Sum([d, Division((d_signs[self.variant], Sum([a, nbs * b]), c))]) watch_rules = 'no negative; decimals distribution; c isnt 1' if self.variant in [174, 175, 178, 179, 181, 183, 185, 187]: watch_rules += '; c isnt deci' self.watch(watch_rules, a, b, c, d) if self.variant in [173, 175, 177, 179, 184, 185, 186, 187]: if self.variant in [173, 177]: f = (a + nbs * b) * c - d elif self.variant in [175, 179]: f = (a + nbs * b) / c - d elif self.variant in [184, 186]: f = d - (a + nbs * b) * c elif self.variant in [185, 187]: f = d - (a + nbs * b) / c self.watch('no negative', f, letters='f') return [a, b, c, d] def __init__(self, build_data, **options): super().setup("minimal", **options) super().setup("numbers", nb=build_data, shuffle_nbs=False, **options) direct_test = options.get('direct_test', False) if not direct_test: super().setup("nb_variants", **options) super().setup('logging', **options) self.adjust_numbers() self.expression = None self.obj = None catalog = dict.fromkeys([i for i in range(24)], self._create_0to23) catalog.update(dict.fromkeys([100, 104], self._create_100_104)) catalog.update(dict.fromkeys([101, 105], self._create_101_105)) catalog.update(dict.fromkeys([102, 106], self._create_102_106)) catalog.update(dict.fromkeys([103, 107], self._create_103_107)) catalog.update(dict.fromkeys([108, 112], self._create_108_112)) catalog.update(dict.fromkeys([109, 113], self._create_109_113)) catalog.update(dict.fromkeys([110, 114], self._create_110_114)) catalog.update(dict.fromkeys([111, 115], self._create_111_115)) catalog.update(dict.fromkeys([116 + i for i in range(8)], self._create_116to123)) catalog.update(dict.fromkeys([124 + i for i in range(8)], self._create_124to131)) catalog.update(dict.fromkeys([132, 133], self._create_132_133)) catalog.update(dict.fromkeys([134, 135], self._create_134_135)) catalog.update(dict.fromkeys([136, 137], self._create_136_137)) catalog.update(dict.fromkeys([138, 139], self._create_138_139)) catalog.update(dict.fromkeys([140 + i for i in range(8)], self._create_140to147)) catalog.update(dict.fromkeys([148 + i for i in range(8)], self._create_148to155)) catalog.update(dict.fromkeys([156 + i for i in range(16)], self._create_156to171)) catalog.update(dict.fromkeys([172 + i for i in range(16)], self._create_172to187)) try: self.abcd = catalog[self.variant]() except KeyError: raise ValueError('Unknown variant identifier for ' 'order_of_operations: {}' .format(str(self.variant))) self.expression = Expression(shared.number_of_the_question, self.obj) self.expression_str = self.expression.printed shared.number_of_the_question += 1 self.transduration = 18
[docs] def q(self, **options): if self.x_layout_variant == 'tabular' or self.slideshow: self.substitutable_question_mark = True return _('{math_expr} = {q_mark}').format( math_expr=shared.machine.write_math_style2(self.obj.printed), q_mark=COLORED_QUESTION_MARK) else: return shared.machine.write_math_style2(self.expression_str)
[docs] def a(self, **options): if self.x_layout_variant == 'tabular' or self.slideshow: return Value(self.expression.right_hand_side.evaluate()).printed else: return shared.machine.write( self.expression.auto_expansion_and_reduction(**options))
[docs] def js_a(self, **kwargs): return [Value(self.expression.right_hand_side.evaluate()).jsprinted]