# -*- 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]