Repository: pyccel/pyccel (python)

Issue:
[SETS] Add C and Fortran support for `copy()`
### Test Code
Provide code which does not currently work but which should do when this issue is fixed:
```python
def foo():
    a = {1,2,1}
    b = a.copy()
```

### Proposed Solution
This feature should be implemented using the functionalities provided by STC and gFTL


Pull request description:
Add support for set.copy
Add C and Fortran support for `set.copy`. Fixes #1919

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b1cc2b1..e4e64b2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,8 +12,6 @@ All notable changes to this project will be documented in this file.
 -   #1700 : Add Python support for list method `sort()`.
 -   #1696 : Add Python support for list method `copy()`.
 -   #1693 : Add Python support for list method `remove()`.
--   #1739 : Add Python support for set method `clear()`.
--   #1740 : Add Python support for set method `copy()`.
 -   #1750 : Add Python support for set method `remove()`.
 -   #1743 : Add Python support for set method `discard()`.
 -   #1754 : Add Python support for set method `update()`.
@@ -38,7 +36,8 @@ All notable changes to this project will be documented in this file.
 -   #1690 : Add C support for list method `pop()`.
 -   #1877 : Add C and Fortran Support for set method `pop()`.
 -   #1917 : Add C and Fortran support for set method `add()`.
--   #1918 : Add C and Fortran support for set method `clear()`.
+-   #1918 : Add support for set method `clear()`.
+-   #1918 : Add support for set method `copy()`.
 -   #1936 : Add missing C output for inline decorator example in documentation
 -   #1937 : Optimise `pyccel.ast.basic.PyccelAstNode.substitute` method.
 -   #1544 : Add support for `typing.TypeAlias`.
diff --git a/docs/builtin-functions.md b/docs/builtin-functions.md
index 2d95cf5c..87e656be 100644
--- a/docs/builtin-functions.md
+++ b/docs/builtin-functions.md
@@ -98,7 +98,7 @@ Python contains a limited number of builtin functions defined [here](https://doc
 |----------|-----------|
 | **`add`** | **Yes** |
 | **`clear`** | **Yes** |
-| `copy` | Python-only |
+| **`copy`** | **Yes** |
 | `difference` | No |
 | `difference_update` | No |
 | `discard` | Python-only |
diff --git a/pyccel/codegen/printing/ccode.py b/pyccel/codegen/printing/ccode.py
index 4a0acd5f..3472a59b 100644
--- a/pyccel/codegen/printing/ccode.py
+++ b/pyccel/codegen/printing/ccode.py
@@ -2524,12 +2524,6 @@ class CCodePrinter(CodePrinter):
     def _print_EmptyNode(self, expr):
         return ''
 
-    def _print_SetAdd(self, expr):
-        var_type = self.get_declare_type(expr.set_variable)
-        set_var = self._print(ObjectAddress(expr.set_variable))
-        arg = self._print(expr.args[0])
-        return f'{var_type}_push({set_var}, {arg});\n'
-
     #=================== OMP ==================
 
     def _print_OmpAnnotatedComment(self, expr):
@@ -2627,6 +2621,17 @@ class CCodePrinter(CodePrinter):
         set_var = self._print(ObjectAddress(expr.set_variable))
         return f'{var_type}_clear({set_var});\n'
 
+    def _print_SetAdd(self, expr):
+        var_type = self.get_declare_type(expr.set_variable)
+        set_var = self._print(ObjectAddress(expr.set_variable))
+        arg = self._print(expr.args[0])
+        return f'{var_type}_push({set_var}, {arg});\n'
+
+    def _print_SetCopy(self, expr):
+        var_type = self.get_declare_type(expr.set_variable)
+        set_var = self._print(expr.set_variable)
+        return f'{var_type}_clone({set_var})'
+
     #=================== MACROS ==================
 
     def _print_MacroShape(self, expr):
diff --git a/pyccel/codegen/printing/fcode.py b/pyccel/codegen/printing/fcode.py
index d05b7dda..7badfaae 100644
--- a/pyccel/codegen/printing/fcode.py
+++ b/pyccel/codegen/printing/fcode.py
@@ -1332,6 +1332,11 @@ class FCodePrinter(CodePrinter):
         self.add_import(self._build_gFTL_extension_module(expr_type))
         return f'{type_name}_pop({var_code})\n'
 
+    def _print_SetCopy(self, expr):
+        var_code = self._print(expr.set_variable)
+        type_name = self._print(expr.class_type)
+        return f'{type_name}({var_code})'
+
     #========================== Numpy Elements ===============================#
 
     def _print_NumpySum(self, expr):


Test patch:
diff --git a/tests/epyccel/test_epyccel_sets.py b/tests/epyccel/test_epyccel_sets.py
index ddfcaa97..c85a0032 100644
--- a/tests/epyccel/test_epyccel_sets.py
+++ b/tests/epyccel/test_epyccel_sets.py
@@ -77,42 +77,46 @@ def test_clear_complex(language):
     python_result = clear_complex()
     assert python_result == pyccel_result
 
-def test_copy_int(python_only_language):
+def test_copy_int(language):
     def copy_int():
         se = {1, 2, 4, 5}
         cop = se.copy()
-        return cop
-    epyccel_copy = epyccel(copy_int, language = python_only_language)
+        size = len(cop)
+        a,b,c,d = cop.pop(), cop.pop(), cop.pop(), cop.pop()
+        return size, len(se), a,b,c,d
+    epyccel_copy = epyccel(copy_int, language = language)
     pyccel_result = epyccel_copy()
     python_result = copy_int()
     assert isinstance(python_result, type(pyccel_result))
-    assert python_result == pyccel_result
-    assert all(isinstance(elem, type(pyccel_result.pop())) for elem in python_result)
+    assert python_result[0] == pyccel_result[0]
+    assert python_result[1] == pyccel_result[1]
+    assert set(python_result[2:]) == set(pyccel_result[2:])
 
 
-def test_copy_float(python_only_language):
+def test_copy_float(language):
     def copy_float():
         se = {5.7, 6.2, 4.3, 9.8}
         cop = se.copy()
-        return cop
-    epyccel_copy = epyccel(copy_float, language = python_only_language)
+        return len(cop), cop.pop(), cop.pop(), cop.pop(), cop.pop(), len(se)
+    epyccel_copy = epyccel(copy_float, language = language)
     pyccel_result = epyccel_copy()
     python_result = copy_float()
     assert isinstance(python_result, type(pyccel_result))
-    assert python_result == pyccel_result
-    assert all(isinstance(elem, type(pyccel_result.pop())) for elem in python_result)
+    assert python_result[0] == pyccel_result[0]
+    assert python_result[-1] == pyccel_result[-1]
+    assert set(python_result[1:-1]) == set(pyccel_result[1:-1])
 
-def test_copy_complex(python_only_language):
+def test_copy_complex(language):
     def copy_complex():
         se = {7j, 6j, 9j}
         cop = se.copy()
-        return cop
-    epyccel_copy = epyccel(copy_complex, language = python_only_language)
+        return len(cop), cop.pop(), cop.pop(), cop.pop()
+    epyccel_copy = epyccel(copy_complex, language = language)
     pyccel_result = epyccel_copy()
     python_result = copy_complex()
     assert isinstance(python_result, type(pyccel_result))
-    assert python_result == pyccel_result
-    assert all(isinstance(elem, type(pyccel_result.pop())) for elem in python_result)
+    assert python_result[0] == pyccel_result[0]
+    assert set(python_result[1:]) == set(pyccel_result[1:])
 
 def test_remove_complex(python_only_language):
     def remove_complex():