Repository: sympy/sympy

Issue:
Permutation is reconstructed wrongly from pickle
# Description

Pickling a `sympy.combinatorics.Permutation` instance and loading the pickle again wrongly reconstructs the object.

Reproduction (in isympy session):
```
In [1]: from sympy.combinatorics import Permutation

In [2]: a = Permutation([2,1,0])

In [3]: import pickle

In [4]: with open("test.pickle", 'wb') as f:
   ...:     pickle.dump(a, f)
   ...: 

In [5]: with open("test.pickle", 'rb') as f:
   ...:     b = pickle.load(f)
   ...: 

In [6]: a
Out[6]: Permutation(0, 2)

In [7]: b
Out[7]: Permutation()

```

# Software versions
Sympy version: 1.12
Python version: 3.11.3
OS: Arch Linux

Also reproducible on <https://live.sympy.org/>

# Additional information
Note that this causes `sympy.tensor.tensor.TensorSymmetry` to be wrongly reconstructed from pickles, and thus no tensorial expression can be pickled. 

It seems this may be related to https://github.com/sympy/sympy/issues/22310, but I don't understand why this is only an issue for `Permutation`, and not, say, `Symbol`.

Discussion:
sympy-bot: :white_check_mark:

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot) (v170). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).



* No release notes entry will be added for this pull request.

<details><summary>Click here to see the pull request description that was parsed.</summary>

    <!-- Your title above should be a short description of what
    was changed. Do not include the issue number in the title. -->

    #### References to other Issues or PRs
    Fixes #25378
    <!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
    format, e.g. "Fixes #1234" (see
    https://tinyurl.com/auto-closing for more information). Also, please
    write a comment on that issue linking back to this pull request once it is
    open. -->


    #### Brief description of what is fixed or changed
    The _args attribute of sympy.core.basic.Basic was not set correctly, then copying permutation (e.g. pickle) would fail when calling __new__ with empty _args.

    #### Release Notes

    <!-- Write the release notes for this release below between the BEGIN and END
    statements. The basic format is a bulleted list with the name of the subpackage
    and the release note for this PR. For example:

    * solvers
      * Added a new solver for logarithmic equations.

    * functions
      * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

    * physics.units
      * Corrected a semantical error in the conversion between volt and statvolt which
        reported the volt as being larger than the statvolt.

    or if no release note(s) should be included use:

    NO ENTRY

    See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
    information on how to write release notes. The bot will check your release
    notes automatically to see if they are formatted correctly. -->

    <!-- BEGIN RELEASE NOTES -->
    NO ENTRY
    <!-- END RELEASE NOTES -->


</details><p>

smichr: All arts of sympy objects should be Basic. I would go with opt 3. Just add another commit that removes that.

Patch:
diff --git a/.mailmap b/.mailmap
index e1e5171465..30c5bbea36 100644
--- a/.mailmap
+++ b/.mailmap
@@ -666,6 +666,7 @@ Hubert Tsang <intsangity@gmail.com>
 Hugo van Kemenade <hugovk@users.noreply.github.com> Hugo <hugovk@users.noreply.github.com>
 Huijun Mai <m.maihuijun@gmail.com>
 Ian Swire <oversizedpenguin@gmail.com>
+Idan Pazi <idan.kp@gmail.com>
 Ilya Pchelintsev <ilya.pchelintsev@outlook.com> ILLIA PCHALINTSAU <ilya.pchelintsev@outlook.com>
 Imran Ahmed Manzoor <imran.manzoor31@gmail.com>
 Ishan Joshi <ishanaj98@gmail.com>
diff --git a/sympy/combinatorics/permutations.py b/sympy/combinatorics/permutations.py
index 6c823720d4..559b43d1b2 100644
--- a/sympy/combinatorics/permutations.py
+++ b/sympy/combinatorics/permutations.py
@@ -1050,6 +1050,12 @@ def _af_new(cls, perm):
         p._size = len(perm)
         return p
 
+    def copy(self):
+        return self.__class__(self.array_form)
+
+    def __getnewargs__(self):
+        return (self.array_form,)
+
     def _hashable_content(self):
         # the array_form (a list) is the Permutation arg, so we need to
         # return a tuple, instead
diff --git a/sympy/core/traversal.py b/sympy/core/traversal.py
index 0615c221a5..6574b12d98 100644
--- a/sympy/core/traversal.py
+++ b/sympy/core/traversal.py
@@ -27,10 +27,7 @@ def iterargs(expr):
     args = [expr]
     for i in args:
         yield i
-        try:
-            args.extend(i.args)
-        except TypeError:
-            pass  # for cases like f being an arg
+        args.extend(i.args)
 
 
 def iterfreeargs(expr, _first=True):
@@ -61,10 +58,7 @@ def iterfreeargs(expr, _first=True):
             for i in iterfreeargs(i.as_dummy(), _first=False):
                 if not i.has(*void):
                     yield i
-        try:
-            args.extend(i.args)
-        except TypeError:
-            pass  # for cases like f being an arg
+        args.extend(i.args)
 
 
 class preorder_traversal:
diff --git a/sympy/ntheory/factor_.py b/sympy/ntheory/factor_.py
index d5d0d02019..40e0b38b08 100644
--- a/sympy/ntheory/factor_.py
+++ b/sympy/ntheory/factor_.py
@@ -8,6 +8,7 @@
 
 from sympy.core import sympify
 from sympy.core.containers import Dict
+from sympy.core.evalf import bitcount
 from sympy.core.expr import Expr
 from sympy.core.function import Function
 from sympy.core.logic import fuzzy_and
@@ -208,12 +209,6 @@ def trailing(n):
     7
     >>> trailing(63)
     0
-
-    See Also
-    ========
-
-    multiplicity
-
     """
     n = abs(int(n))
     if not n:
@@ -222,28 +217,32 @@ def trailing(n):
     if low_byte:
         return small_trailing[low_byte]
 
-    t = 8
-    n >>= 8
     # 2**m is quick for z up through 2**30
-    z = n.bit_length() - 1
-    if n == 1 << z:
-        return z + t
+    z = bitcount(n) - 1
+    if isinstance(z, SYMPY_INTS):
+        if n == 1 << z:
+            return z
 
     if z < 300:
         # fixed 8-byte reduction
+        t = 8
+        n >>= 8
         while not n & 0xff:
             n >>= 8
             t += 8
-    else:
-        # binary reduction important when there might be a large
-        # number of trailing 0s
-        p = z >> 1
-        while not n & 0xff:
-            while n & ((1 << p) - 1):
-                p >>= 1
+        return t + small_trailing[n & 0xff]
+
+    # binary reduction important when there might be a large
+    # number of trailing 0s
+    t = 0
+    p = 8
+    while not n & 1:
+        while not n & ((1 << p) - 1):
             n >>= p
             t += p
-    return t + small_trailing[n & 0xff]
+            p *= 2
+        p //= 2
+    return t
 
 
 def multiplicity(p, n):
@@ -273,11 +272,6 @@ def multiplicity(p, n):
     >>> _ == multiplicity_in_factorial(p, n)
     True
 
-    See Also
-    ========
-
-    trailing
-
     """
     try:
         p, n = as_int(p), as_int(n)


Test patch:
diff --git a/sympy/combinatorics/tests/test_permutations.py b/sympy/combinatorics/tests/test_permutations.py
index efd40666ab..b52fcfec0e 100644
--- a/sympy/combinatorics/tests/test_permutations.py
+++ b/sympy/combinatorics/tests/test_permutations.py
@@ -1,4 +1,5 @@
 from itertools import permutations
+from copy import copy
 
 from sympy.core.expr import unchanged
 from sympy.core.numbers import Integer
@@ -29,6 +30,8 @@ def test_Permutation():
     raises(TypeError, lambda: p(5))
     # conversion to list
     assert list(p) == list(range(4))
+    assert p.copy() == p
+    assert copy(p) == p
     assert Permutation(size=4) == Permutation(3)
     assert Permutation(Permutation(3), size=5) == Permutation(4)
     # cycle form with size
diff --git a/sympy/ntheory/tests/test_factor_.py b/sympy/ntheory/tests/test_factor_.py
index 995062aae8..626dde0dec 100644
--- a/sympy/ntheory/tests/test_factor_.py
+++ b/sympy/ntheory/tests/test_factor_.py
@@ -76,9 +76,6 @@ def test_trailing_bitcount():
     for i in range(100):
         assert trailing(1 << i) == i
         assert trailing((1 << i) * 31337) == i
-    for i in range(500):
-        n = (1 << 500) + (1 << i)
-        assert trailing(n) == i
     assert trailing(1 << 1000001) == 1000001
     assert trailing((1 << 273956)*7**37) == 273956
     # issue 12709