Commit 140ddc59 authored by Taddeus Kroes's avatar Taddeus Kroes

Updated all unit tests correspondingly to the previous commits.

parent 5445504d
......@@ -4,8 +4,7 @@ from tests.rulestestcase import RulesTestCase as TestCase, rewrite
class TestLeidenOefenopgave(TestCase):
def test_1_1(self):
self.assertRewrite([
'-5(x2 - 3x + 6)',
'-(5(x ^ 2 - 3x) + 5 * 6)',
'-5(x ^ 2 - 3x + 6)',
'-(5x ^ 2 + 5(-3x) + 5 * 6)',
'-(5x ^ 2 - 5 * 3x + 5 * 6)',
'-(5x ^ 2 - 15x + 5 * 6)',
......@@ -34,23 +33,21 @@ class TestLeidenOefenopgave(TestCase):
'(x ^ 2 + x * 1 + 1x + 1 * 1)(x + 1)',
'(x ^ 2 + x + 1x + 1 * 1)(x + 1)',
'(x ^ 2 + x + x + 1 * 1)(x + 1)',
'(x ^ 2 + (1 + 1)x + 1 * 1)(x + 1)',
'(x ^ 2 + 2x + 1 * 1)(x + 1)',
'(x ^ 2 + x + x + 1)(x + 1)',
'(x ^ 2 + (1 + 1)x + 1)(x + 1)',
'(x ^ 2 + 2x + 1)(x + 1)',
'(x ^ 2 + 2x)x + (x ^ 2 + 2x)1 + 1x + 1 * 1',
'xx ^ 2 + x * 2x + (x ^ 2 + 2x)1 + 1x + 1 * 1',
'x ^ (1 + 2) + x * 2x + (x ^ 2 + 2x)1 + 1x + 1 * 1',
'x ^ 3 + x * 2x + (x ^ 2 + 2x)1 + 1x + 1 * 1',
'x ^ 3 + x ^ (1 + 1)2 + (x ^ 2 + 2x)1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 * 2 + (x ^ 2 + 2x)1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 * 2 + 1x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + x ^ 2 * 2 + x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + (2 + 1)x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + 3x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + 3x ^ 2 + 2x + 1x + 1 * 1',
'x ^ 3 + 3x ^ 2 + 2x + x + 1 * 1',
'x ^ 3 + 3x ^ 2 + (2 + 1)x + 1 * 1',
'x ^ 3 + 3x ^ 2 + 3x + 1 * 1',
'x ^ 2 * x + x ^ 2 * 1 + 2xx + 2x * 1 + 1x + 1 * 1',
'x ^ (2 + 1) + x ^ 2 * 1 + 2xx + 2x * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 * 1 + 2xx + 2x * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 + 2xx + 2x * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 + 2x ^ (1 + 1) + 2x * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 + 2x ^ 2 + 2x * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 + 2x ^ 2 + 2x + 1x + 1 * 1',
'x ^ 3 + x ^ 2 + 2x ^ 2 + 2x + x + 1 * 1',
'x ^ 3 + x ^ 2 + 2x ^ 2 + 2x + x + 1',
'x ^ 3 + (1 + 2)x ^ 2 + 2x + x + 1',
'x ^ 3 + 3x ^ 2 + 2x + x + 1',
'x ^ 3 + 3x ^ 2 + (2 + 1)x + 1',
'x ^ 3 + 3x ^ 2 + 3x + 1',
])
......@@ -63,8 +60,8 @@ class TestLeidenOefenopgave(TestCase):
'x ^ 2 + x * 1 + 1x + 1 * 1',
'x ^ 2 + x + 1x + 1 * 1',
'x ^ 2 + x + x + 1 * 1',
'x ^ 2 + (1 + 1)x + 1 * 1',
'x ^ 2 + 2x + 1 * 1',
'x ^ 2 + x + x + 1',
'x ^ 2 + (1 + 1)x + 1',
'x ^ 2 + 2x + 1',
])
......@@ -80,10 +77,11 @@ class TestLeidenOefenopgave(TestCase):
'x ^ 2 - x + (-1)x + (-1)(-1)',
'x ^ 2 - x - 1x + (-1)(-1)',
'x ^ 2 - x - x + (-1)(-1)',
'x ^ 2 + (1 + 1)(-x) + (-1)(-1)',
'x ^ 2 + 2(-x) + (-1)(-1)',
'x ^ 2 - 2x + (-1)(-1)',
'x ^ 2 - 2x - -1',
'x ^ 2 - x - x - -1',
'x ^ 2 - x - x + 1',
'x ^ 2 + (1 + 1)(-x) + 1',
'x ^ 2 + 2(-x) + 1',
'x ^ 2 - 2x + 1',
'x ^ 2 - 2x + 1',
])
......@@ -145,7 +143,6 @@ class TestLeidenOefenopgave(TestCase):
'-20x + 16x ^ 2 - 25 + 20x',
'(-1 + 1)20x + 16x ^ 2 - 25',
'0 * 20x + 16x ^ 2 - 25',
'0x + 16x ^ 2 - 25',
'0 + 16x ^ 2 - 25',
'16x ^ 2 - 25',
])
......
......@@ -5,7 +5,6 @@ class TestLeidenOefenopgaveV12(TestCase):
def test_1_a(self):
self.assertRewrite([
'-5(x2 - 3x + 6)',
'-(5(x ^ 2 - 3x) + 5 * 6)',
'-(5x ^ 2 + 5(-3x) + 5 * 6)',
'-(5x ^ 2 - 5 * 3x + 5 * 6)',
'-(5x ^ 2 - 15x + 5 * 6)',
......@@ -30,31 +29,29 @@ class TestLeidenOefenopgaveV12(TestCase):
'-(2 * 6x + 2(-4))(6x - 4)x',
'-(12x + 2(-4))(6x - 4)x',
'-(12x - 8)(6x - 4)x',
'-(12x * 6x + 12x(-4) + (-8)6x + (-8)(-4))x',
'-(72xx + 12x(-4) + (-8)6x + (-8)(-4))x',
'-(72x ^ (1 + 1) + 12x(-4) + (-8)6x + (-8)(-4))x',
'-(72x ^ 2 + 12x(-4) + (-8)6x + (-8)(-4))x',
'-(72x ^ 2 + (-48)x + (-8)6x + (-8)(-4))x',
'-(72x ^ 2 - 48x + (-8)6x + (-8)(-4))x',
'-(72x ^ 2 - 48x + (-48)x + (-8)(-4))x',
'-(72x ^ 2 - 48x - 48x + (-8)(-4))x',
'-(72x ^ 2 + (1 + 1)(-48x) + (-8)(-4))x',
'-(72x ^ 2 + 2(-48x) + (-8)(-4))x',
'-(72x ^ 2 - 2 * 48x + (-8)(-4))x',
'-(72x ^ 2 - 96x + (-8)(-4))x',
'-(72x ^ 2 - 96x - -32)x',
'-(72x ^ 2 - 96x + 32)x',
'-(x(72x ^ 2 - 96x) + x * 32)',
'-(x * 72x ^ 2 + x(-96x) + x * 32)',
'-(x ^ (1 + 2)72 + x(-96x) + x * 32)',
'-(x ^ 3 * 72 + x(-96x) + x * 32)',
'-(x ^ 3 * 72 - x * 96x + x * 32)',
'-(x ^ 3 * 72 - x ^ (1 + 1)96 + x * 32)',
'-(x ^ 3 * 72 - x ^ 2 * 96 + x * 32)',
'-x ^ 3 * 72 - -x ^ 2 * 96 - x * 32',
'-x ^ 3 * 72 + x ^ 2 * 96 - x * 32',
'-72x ^ 3 + x ^ 2 * 96 - x * 32',
'-72x ^ 3 + 96x ^ 2 - x * 32',
'-(12x - 8)(6xx + (-4)x)',
'-(12x - 8)(6x ^ (1 + 1) + (-4)x)',
'-(12x - 8)(6x ^ 2 + (-4)x)',
'-(12x - 8)(6x ^ 2 - 4x)',
'-(12x * 6x ^ 2 + 12x(-4x) + (-8)6x ^ 2 + (-8)(-4x))',
'-(72xx ^ 2 + 12x(-4x) + (-8)6x ^ 2 + (-8)(-4x))',
'-(72x ^ (1 + 2) + 12x(-4x) + (-8)6x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 + 12x(-4x) + (-8)6x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 - 12x * 4x + (-8)6x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 - 48xx + (-8)6x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 - 48x ^ (1 + 1) + (-8)6x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 - 48x ^ 2 + (-8)6x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 - 48x ^ 2 + (-48)x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 - 48x ^ 2 - 48x ^ 2 + (-8)(-4x))',
'-(72x ^ 3 - 48x ^ 2 - 48x ^ 2 - 8(-4x))',
'-(72x ^ 3 - 48x ^ 2 - 48x ^ 2 - -8 * 4x)',
'-(72x ^ 3 - 48x ^ 2 - 48x ^ 2 - -32x)',
'-(72x ^ 3 - 48x ^ 2 - 48x ^ 2 + 32x)',
'-(72x ^ 3 + (1 + 1)(-48x ^ 2) + 32x)',
'-(72x ^ 3 + 2(-48x ^ 2) + 32x)',
'-(72x ^ 3 - 2 * 48x ^ 2 + 32x)',
'-(72x ^ 3 - 96x ^ 2 + 32x)',
'-72x ^ 3 - -96x ^ 2 - 32x',
'-72x ^ 3 + 96x ^ 2 - 32x',
])
......
......@@ -190,8 +190,7 @@ class TestNode(RulesTestCase):
self.assertEqual(self.scope.nodes, [self.a, self.b])
def test_scope_remove_error(self):
with self.assertRaises(ValueError):
self.scope.remove(self.f)
self.assertRaises(ValueError, self.scope.remove, self.f)
def test_scope_replace(self):
self.scope.replace(self.cd, self.f)
......@@ -305,4 +304,4 @@ class TestNode(RulesTestCase):
a = tree('a')
self.assertEqual(negation_to_node(-a), N('-', a))
self.assertEqual(negation_to_node(-(a + 1)), N('-', a + 1))
self.assertEqual(negation_to_node(-(a - 1)), N('-', a + N('-', 1)))
self.assertEqual(negation_to_node(-(a - 1)), N('-', a + N('-', L(1))))
# vim: set fileencoding=utf-8 :
import unittest
from src.parser import Parser
from src.parser import Parser, find_possibilities
from src.node import ExpressionNode as Node, ExpressionLeaf as Leaf, \
SPECIAL_TOKENS, sin, cos, der, log, ln, integral, indef, absolute
SPECIAL_TOKENS, sin, cos, der, log, ln, integral, indef, absolute, \
Scope
from src.possibilities import Possibility as P
from src.rules.numerics import add_numerics
from tests.parser import ParserWrapper, run_expressions, line, graph
from tests.rulestestcase import tree
from tests.rulestestcase import RulesTestCase, tree
class TestParser(unittest.TestCase):
class TestParser(RulesTestCase):
def test_constructor(self):
node = Node('+', Leaf(1), Leaf(4))
self.assertEqual(ParserWrapper(Parser).run(['1 + 4']), node)
......@@ -29,10 +30,12 @@ class TestParser(unittest.TestCase):
def test_reset_after_failure(self):
parser = ParserWrapper(Parser)
parser.run(['-(3a+6b)'])
parser.parser.find_possibilities()
possibilities1 = parser.parser.possibilities
self.assertNotEqual(possibilities1, [])
parser.run(['5+2*6'])
parser.parser.find_possibilities()
possibilities2 = parser.parser.possibilities
self.assertNotEqual(possibilities2, [])
......@@ -139,3 +142,18 @@ class TestParser(unittest.TestCase):
self.assertEqual(tree('|x|'), absolute(x))
self.assertEqual(tree('|x2|'), absolute(x ** 2))
def test_find_possibilities_basic(self):
l1, l2 = root = tree('1 + 2')
self.assertEqual(find_possibilities(root),
[P(root, add_numerics, (Scope(root), l1, l2))])
def test_find_possibilities_duplicates(self):
(l1, l2), l3 = root = tree('1 + 2 + 3')
self.assertEqual(find_possibilities(root),
[P(root, add_numerics, (Scope(root), l1, l2)),
P(root, add_numerics, (Scope(root), l1, l3)),
P(root, add_numerics, (Scope(root), l2, l3))])
def test_no_expression_error(self):
self.assertRaises(RuntimeError, ParserWrapper(Parser).run, ['', '?'])
import unittest
from src.possibilities import MESSAGES, Possibility as P, filter_duplicates
from src.possibilities import MESSAGES, Possibility as P
from src.rules.numerics import add_numerics
from tests.rulestestcase import tree
......@@ -41,7 +41,7 @@ class TestPossibilities(unittest.TestCase):
def test_multiple_input(self):
parser = ParserWrapper(Parser)
parser.run(['1+2', '3+4'])
parser.run(['1+2', '?', '3+4', '?'])
possibilities = parser.parser.possibilities
self.assertEqual('\n'.join([repr(pos) for pos in possibilities]),
'<Possibility root="3 + 4" handler=add_numerics' \
......@@ -49,7 +49,7 @@ class TestPossibilities(unittest.TestCase):
def test_multiple_runs(self):
parser = ParserWrapper(Parser)
parser.run(['1+2'])
parser.run(['1+2', '?'])
possibilities = parser.parser.possibilities
self.assertEqual('\n'.join([repr(pos) for pos in possibilities]),
'<Possibility root="1 + 2" handler=add_numerics' \
......@@ -58,32 +58,32 @@ class TestPossibilities(unittest.TestCase):
# Remove previous possibilities after second run() call.
parser.run(['', ' '])
possibilities = parser.parser.possibilities
self.assertEqual(possibilities, [])
self.assertEqual(possibilities, None)
# Overwrite previous possibilities with new ones
parser.run(['3+4'])
parser.run(['3+4', '?'])
possibilities = parser.parser.possibilities
self.assertEqual('\n'.join([repr(pos) for pos in possibilities]),
'<Possibility root="3 + 4" handler=add_numerics' \
' args=(<Scope of "3 + 4">, 3, 4)>')
def test_filter_duplicates(self):
a, b = ab = tree('a + b')
p0 = P(a, dummy_handler, (1, 2))
p1 = P(ab, dummy_handler, (1, 2))
p2 = P(ab, dummy_handler, (1, 2, 3))
p3 = P(ab, dummy_handler_msg, (1, 2))
self.assertEqual(filter_duplicates([]), [])
self.assertEqual(filter_duplicates([p0, p1]), [p1])
self.assertEqual(filter_duplicates([p1, p2]), [p1, p2])
self.assertEqual(filter_duplicates([p1, p3]), [p1, p3])
self.assertEqual(filter_duplicates([p0, p1, p2, p3]), [p1, p2, p3])
# Docstrings example
(l1, l2), l3 = left, l3 = right = tree('1 + 2 + 3')
p0 = P(left, add_numerics, (1, 2, 1, 2))
p1 = P(right, add_numerics, (1, 2, 1, 2))
p2 = P(right, add_numerics, (1, 3, 1, 3))
p3 = P(right, add_numerics, (2, 3, 2, 3))
self.assertEqual(filter_duplicates([p0, p1, p2, p3]), [p1, p2, p3])
#def test_filter_duplicates(self):
# a, b = ab = tree('a + b')
# p0 = P(a, dummy_handler, (1, 2))
# p1 = P(ab, dummy_handler, (1, 2))
# p2 = P(ab, dummy_handler, (1, 2, 3))
# p3 = P(ab, dummy_handler_msg, (1, 2))
# self.assertEqual(filter_duplicates([]), [])
# self.assertEqual(filter_duplicates([p0, p1]), [p1])
# self.assertEqual(filter_duplicates([p1, p2]), [p1, p2])
# self.assertEqual(filter_duplicates([p1, p3]), [p1, p3])
# self.assertEqual(filter_duplicates([p0, p1, p2, p3]), [p1, p2, p3])
# # Docstrings example
# (l1, l2), l3 = left, l3 = right = tree('1 + 2 + 3')
# p0 = P(left, add_numerics, (1, 2, 1, 2))
# p1 = P(right, add_numerics, (1, 2, 1, 2))
# p2 = P(right, add_numerics, (1, 3, 1, 3))
# p3 = P(right, add_numerics, (2, 3, 2, 3))
# self.assertEqual(filter_duplicates([p0, p1, p2, p3]), [p1, p2, p3])
......@@ -103,21 +103,18 @@ class TestRulesDerivatives(RulesTestCase):
self.assertRewrite([
"[x ^ x]'",
"[e ^ ln(x ^ x)]'",
"[e ^ (xln(x))]'",
"e ^ (xln(x))ln(e)[xln(x)]'",
"e ^ (xln(x))1[xln(x)]'",
"e ^ (xln(x))[xln(x)]'",
"e ^ (xln(x))([x]' * ln(x) + x[ln(x)]')",
"e ^ (xln(x))(1ln(x) + x[ln(x)]')",
"e ^ (xln(x))(ln(x) + x[ln(x)]')",
"e ^ (xln(x))(ln(x) + x(1 / (xln(e))))",
"e ^ (xln(x))(ln(x) + x(1 / (x * 1)))",
"e ^ (xln(x))(ln(x) + x(1 / x))",
"e ^ (xln(x))(ln(x) + 1x / x)",
"e ^ (xln(x))(ln(x) + x / x)",
"e ^ (xln(x))(ln(x) + 1)",
"e ^ ln(x ^ x)(ln(x) + 1)",
# FIXME: "x ^ x(ln(x) + 1)", -> needs strategy
"e ^ ln(x ^ x)[ln(x ^ x)]'",
"x ^ x * [ln(x ^ x)]'",
"x ^ x * [xln(x)]'",
"x ^ x * ([x]' * ln(x) + x[ln(x)]')",
"x ^ x * (1ln(x) + x[ln(x)]')",
"x ^ x * (ln(x) + x[ln(x)]')",
"x ^ x * (ln(x) + x(1 / x))",
"x ^ x * (ln(x) + 1x / x)",
"x ^ x * (ln(x) + x / x)",
"x ^ x * (ln(x) + 1)",
"x ^ x * ln(x) + x ^ x * 1",
"x ^ x * ln(x) + x ^ x",
])
def test_variable_root(self):
......@@ -130,6 +127,10 @@ class TestRulesDerivatives(RulesTestCase):
g, x = root[0]
self.assertEqual(variable_exponent(root, ()), g ** x * ln(g))
root = tree('der(e ^ x)')
e, x = root[0]
self.assertEqual(variable_exponent(root, ()), e ** x)
def test_chain_rule(self):
root = tree('der(2 ^ x ^ 3)')
l2, x3 = root[0]
......@@ -150,6 +151,9 @@ class TestRulesDerivatives(RulesTestCase):
root, x, l1, l10 = tree('der(log(x)), x, 1, 10')
self.assertEqual(logarithmic(root, ()), l1 / (x * ln(l10)))
root, x, l1, l10 = tree('der(ln(x)), x, 1, 10')
self.assertEqual(logarithmic(root, ()), l1 / x)
def test_match_goniometric(self):
root = tree('der(sin(x))')
self.assertEqualPos(match_goniometric(root), [P(root, sinus)])
......@@ -252,10 +256,10 @@ class TestRulesDerivatives(RulesTestCase):
self.assertEqual(quotient_rule(root, ()),
(der(f) * g - f * der(g)) / g ** 2)
def test_natural_pase_chain(self):
self.assertRewrite([
'der(e ^ x)',
'e ^ x * ln(e)',
'e ^ x * 1',
'e ^ x',
])
#def test_natural_pase_chain(self):
# self.assertRewrite([
# 'der(e ^ x)',
# 'e ^ x * ln(e)',
# 'e ^ x * 1',
# 'e ^ x',
# ])
from src.rules.factors import match_expand, expand_single, expand_double
from src.rules.factors import match_expand, expand_double, expand_single
from src.node import Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -16,17 +16,23 @@ class TestRulesFactors(RulesTestCase):
root = bc * a
self.assertEqualPos(match_expand(root),
[P(root, expand_single, (Scope(root), a, bc))])
[P(root, expand_single, (Scope(root), bc, a))])
root = a * d * bc
root = a * bc * d
self.assertEqualPos(match_expand(root),
[P(root, expand_single, (Scope(root), a, bc)),
P(root, expand_single, (Scope(root), d, bc))])
P(root, expand_single, (Scope(root), bc, d))])
ab, cd = root = (a + b) * (c + d)
self.assertEqualPos(match_expand(root),
[P(root, expand_double, (Scope(root), ab, cd))])
(ab, cd), e = root = tree('(a + b)(c + d)e')
self.assertEqualPos(match_expand(root),
[P(root, expand_double, (Scope(root), ab, cd)),
P(root, expand_single, (Scope(root), cd, e)),
P(root, expand_single, (Scope(root), ab, e))])
def test_expand_single(self):
a, b, c, d = tree('a,b,c,d')
bc = b + c
......@@ -35,9 +41,9 @@ class TestRulesFactors(RulesTestCase):
self.assertEqualNodes(expand_single(root, (Scope(root), a, bc)),
a * b + a * c)
root = a * d * bc
self.assertEqualNodes(expand_single(root, (Scope(root), a, bc)),
(a * b + a * c) * d)
root = a * bc * d
self.assertEqualNodes(expand_single(root, (Scope(root), bc, d)),
a * (b * d + c * d))
def test_expand_double(self):
(a, b), (c, d) = ab, cd = tree('a + b,c + d')
......
......@@ -87,7 +87,6 @@ class TestRulesLineq(RulesTestCase):
'5x = -3x - 5 + 3x',
'5x = (-1 + 1)3x - 5',
'5x = 0 * 3x - 5',
'5x = 0x - 5',
'5x = 0 - 5',
'5x = -5',
'5x / 5 = (-5) / 5',
......
......@@ -13,7 +13,8 @@ from tests.rulestestcase import RulesTestCase, tree
class TestRulesLogarithmic(RulesTestCase):
def test_match_constant_logarithm(self):
self.assertRaises(ValueError, tree, 'log_1(a)')
self.assertRaises(ValueError, match_constant_logarithm,
tree('log_1(a)'))
root = tree('log 1')
self.assertEqualPos(match_constant_logarithm(root),
......
from src.rules.numerics import match_add_numerics, add_numerics, \
match_divide_numerics, divide_numerics, reduce_fraction_constants, \
match_multiply_numerics, multiply_numerics, \
raise_numerics
match_multiply_numerics, multiply_numerics, multiply_zero, \
multiply_one, raise_numerics
from src.node import ExpressionLeaf as L, Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -110,6 +110,20 @@ class TestRulesNumerics(RulesTestCase):
self.assertEqual(match_multiply_numerics(root),
[P(root, multiply_numerics, (Scope(root), f3, f2))])
def test_match_multiply_zero(self):
l0, x = root = tree('0x')
self.assertEqual(match_multiply_numerics(root),
[P(root, multiply_zero, (l0,))])
def test_match_multiply_one(self):
l1, x = root = tree('1x')
self.assertEqual(match_multiply_numerics(root),
[P(root, multiply_one, (Scope(root), l1))])
(x, l1), x = root = tree('x * 1x')
self.assertEqual(match_multiply_numerics(root),
[P(root, multiply_one, (Scope(root), l1))])
def test_multiply_numerics(self):
a, b, i2, i3, i6, f2, f3, f6 = tree('a,b,2,3,6,2.0,3.0,6.0')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment