Repository: sympy/sympy

Issue:
ascii pprint of ProductSet uses non-ascii multiplication symbol
````
>>> srepr(A)
'ProductSet(FiniteSet(Rational(13, 111), Integer(2), Integer(3)), FiniteSet(exp(Integer(10)), Mul(Rational(1, 2), pi)))'

>>> pprint(A, use_unicode=True)
⎧ 13      ⎫   ⎧π   10⎫
⎨───, 2, 3⎬ × ⎨─, ℯ  ⎬
⎩111      ⎭   ⎩2     ⎭

>>> pprint(A, use_unicode=False)
  13           pi   10 
{---, 2, 3} × {--, e  }
 111           2       
````

Last one should be `x` not `\xd7`.  See `printing/pretty/pretty.py`.

Possible fix might be:
`prod_char = U('MULTIPLICATION SIGN') if self._use_unicode else 'x'`

Also needs a test---can be simplier than mine.

Discussion:

Patch:
diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py
index b1565fd52f..720abd62d3 100644
--- a/sympy/combinatorics/perm_groups.py
+++ b/sympy/combinatorics/perm_groups.py
@@ -123,10 +123,10 @@ def __new__(cls, *args, **kwargs):
         """The default constructor. Accepts Cycle and Permutation forms.
         Removes duplicates unless ``dups`` keyword is False.
         """
+        args = list(args[0] if is_sequence(args[0]) else args)
         if not args:
-            args = [Permutation()]
-        else:
-            args = list(args[0] if is_sequence(args[0]) else args)
+            raise ValueError('must supply one or more permutations '
+            'to define the group')
         if any(isinstance(a, Cycle) for a in args):
             args = [Permutation(a) for a in args]
         if has_variety(a.size for a in args):
@@ -1568,7 +1568,7 @@ def is_nilpotent(self):
         else:
             return self._is_nilpotent
 
-    def is_normal(self, gr, strict=True):
+    def is_normal(self, gr):
         """Test if G=self is a normal subgroup of gr.
 
         G is normal in gr if
@@ -1590,20 +1590,12 @@ def is_normal(self, gr, strict=True):
         True
 
         """
-        d_self = self.degree
-        d_gr = gr.degree
-        new_self = self.copy()
-        if not strict and d_self != d_gr:
-            if d_self < d_gr:
-                new_self = PermGroup(new_self.generators + [Permutation(d_gr - 1)])
-            else:
-                gr = PermGroup(gr.generators + [Permutation(d_self - 1)])
-        gens2 = [p._array_form for p in new_self.generators]
+        gens2 = [p._array_form for p in self.generators]
         gens1 = [p._array_form for p in gr.generators]
         for g1 in gens1:
             for g2 in gens2:
                 p = _af_rmuln(g1, g2, _af_invert(g1))
-                if not new_self.coset_factor(p, True):
+                if not self.coset_factor(p, True):
                     return False
         return True
 
diff --git a/sympy/combinatorics/permutations.py b/sympy/combinatorics/permutations.py
index cf68727df8..7e7a052247 100644
--- a/sympy/combinatorics/permutations.py
+++ b/sympy/combinatorics/permutations.py
@@ -235,7 +235,7 @@ class Cycle(dict):
     Wrapper around dict which provides the functionality of a disjoint cycle.
 
     A cycle shows the rule to use to move subsets of elements to obtain
-    a permutation. The Cycle class is more flexible than Permutation in
+    a permutation. The Cycle class is more flexible that Permutation in
     that 1) all elements need not be present in order to investigate how
     multiple cycles act in sequence and 2) it can contain singletons:
 
diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py
index 034c977ef6..555d318930 100644
--- a/sympy/printing/pretty/pretty.py
+++ b/sympy/printing/pretty/pretty.py
@@ -1444,7 +1444,7 @@ def _print_ProductSet(self, p):
             from sympy import Pow
             return self._print(Pow(p.sets[0], len(p.sets), evaluate=False))
         else:
-            prod_char = u('\xd7')
+            prod_char = u("\N{MULTIPLICATION SIGN}") if self._use_unicode else 'x'
             return self._print_seq(p.sets, None, None, ' %s ' % prod_char,
                                    parenthesize=lambda set: set.is_Union or
                                    set.is_Intersection or set.is_ProductSet)


Test patch:
diff --git a/sympy/combinatorics/tests/test_perm_groups.py b/sympy/combinatorics/tests/test_perm_groups.py
index 3f45083ba7..d8f7118cda 100644
--- a/sympy/combinatorics/tests/test_perm_groups.py
+++ b/sympy/combinatorics/tests/test_perm_groups.py
@@ -60,7 +60,6 @@ def test_order():
     b = Permutation([2, 1, 3, 4, 5, 6, 7, 8, 9, 0])
     g = PermutationGroup([a, b])
     assert g.order() == 1814400
-    assert PermutationGroup().order() == 1
 
 
 def test_equality():
@@ -274,10 +273,6 @@ def test_is_normal():
     assert G1.is_subgroup(G6)
     assert not G1.is_subgroup(G4)
     assert G2.is_subgroup(G4)
-    s4 = PermutationGroup(Permutation(0, 1, 2, 3), Permutation(3)(0, 1))
-    s6 = PermutationGroup(Permutation(0, 1, 2, 3, 5), Permutation(5)(0, 1))
-    assert s6.is_normal(s4, strict=False)
-    assert not s4.is_normal(s6, strict=False)
 
 
 def test_eq():
@@ -722,7 +717,3 @@ def test_elements():
 def test_is_group():
     assert PermutationGroup(Permutation(1,2), Permutation(2,4)).is_group == True
     assert SymmetricGroup(4).is_group == True
-
-
-def test_PermutationGroup():
-    assert PermutationGroup() == PermutationGroup(Permutation())
diff --git a/sympy/printing/pretty/tests/test_pretty.py b/sympy/printing/pretty/tests/test_pretty.py
index 0724493609..ea40affa5c 100644
--- a/sympy/printing/pretty/tests/test_pretty.py
+++ b/sympy/printing/pretty/tests/test_pretty.py
@@ -3147,6 +3147,13 @@ def test_ProductSet_paranthesis():
     a, b, c = Interval(2, 3), Interval(4, 7), Interval(1, 9)
     assert upretty(Union(a*b, b*FiniteSet(1, 2))) == ucode_str
 
+def test_ProductSet_prod_char_issue_10413():
+    ascii_str = '[2, 3] x [4, 7]'
+    ucode_str = u('[2, 3] × [4, 7]')
+
+    a, b = Interval(2, 3), Interval(4, 7)
+    assert pretty(a*b) == ascii_str
+    assert upretty(a*b) == ucode_str
 
 def test_pretty_sequences():
     s1 = SeqFormula(a**2, (0, oo))