csignum-fast 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Alexandru Colesnicov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include signum.cpp
2
+ include README.md
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.4
2
+ Name: csignum-fast
3
+ Version: 1.0.0
4
+ Summary: High-performance universal sign function
5
+ Author-email: Alexandru Colesnicov <acolesnicov@gmx.com>
6
+ License-Expression: MIT
7
+ Keywords: math,signum,sign,c-extension
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Libraries
11
+ Classifier: Programming Language :: C
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # csignum-fast
19
+ ![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)
20
+ ![License](https://img.shields.io/badge/license-MIT-green.svg)
21
+ ![Status](https://img.shields.io/badge/release-Christmas%20Edition-red.svg)
22
+ ![Tests](https://img.shields.io/badge/tests-51%20passed-brightgreen.svg)
23
+
24
+ **A robust, high-performance C++ implementation of the universal sign function for Python.**
25
+ *Released on December 25, 2025 — The Christmas Edition.*
26
+
27
+ ## Key Features
28
+
29
+ 1. **Uniform Results**: Always returns only `-1`, `0`, or `1` as an `int` for valid numeric comparisons.
30
+ 2. **Correct Edge Case Handling**:
31
+ * `sign(+0.0)` and `sign(-0.0)` return `0`.
32
+ * `sign(inf)` returns `1`, `sign(-inf)` returns `-1`.
33
+ * For any **NaN** (float NaN, Decimal NaN, etc.), it returns `math.nan` (float).
34
+ 3. **Comprehensive Duck Typing**: Delegates comparisons to the argument's class. Works seamlessly with:
35
+ * Built-in `int` (including arbitrary-precision), `bool`, and `float`.
36
+ * `fractions.Fraction` and `decimal.Decimal`.
37
+ * Any existing and future objects that support rich comparisons with numbers.
38
+ 4. **Informative Error Handling for Easy Debugging**: Provides clear, descriptive `TypeError` messages when passed non-numeric, non-scalar, or incomparable arguments.
39
+ 5. **High Performance**: Implemented in C++ using a branchless ternary logic approach (no `if-else` chains), ensuring maximum execution speed.
40
+ 6. **Thoroughly Tested**: Tested on 51 cases including different types, edge cases, new custom class, and inappropriate arguments.
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install csignum-fast
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ```python
51
+ from signum import sign
52
+ from decimal import Decimal
53
+
54
+ print(sign(-10**100)) # -1
55
+ print(sign(3.14)) # 1
56
+ print(sign(Decimal("0.0"))) # 0
57
+ print(sign(float('-nan'))) # nan
58
+ ```
@@ -0,0 +1,41 @@
1
+ # csignum-fast
2
+ ![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)
3
+ ![License](https://img.shields.io/badge/license-MIT-green.svg)
4
+ ![Status](https://img.shields.io/badge/release-Christmas%20Edition-red.svg)
5
+ ![Tests](https://img.shields.io/badge/tests-51%20passed-brightgreen.svg)
6
+
7
+ **A robust, high-performance C++ implementation of the universal sign function for Python.**
8
+ *Released on December 25, 2025 — The Christmas Edition.*
9
+
10
+ ## Key Features
11
+
12
+ 1. **Uniform Results**: Always returns only `-1`, `0`, or `1` as an `int` for valid numeric comparisons.
13
+ 2. **Correct Edge Case Handling**:
14
+ * `sign(+0.0)` and `sign(-0.0)` return `0`.
15
+ * `sign(inf)` returns `1`, `sign(-inf)` returns `-1`.
16
+ * For any **NaN** (float NaN, Decimal NaN, etc.), it returns `math.nan` (float).
17
+ 3. **Comprehensive Duck Typing**: Delegates comparisons to the argument's class. Works seamlessly with:
18
+ * Built-in `int` (including arbitrary-precision), `bool`, and `float`.
19
+ * `fractions.Fraction` and `decimal.Decimal`.
20
+ * Any existing and future objects that support rich comparisons with numbers.
21
+ 4. **Informative Error Handling for Easy Debugging**: Provides clear, descriptive `TypeError` messages when passed non-numeric, non-scalar, or incomparable arguments.
22
+ 5. **High Performance**: Implemented in C++ using a branchless ternary logic approach (no `if-else` chains), ensuring maximum execution speed.
23
+ 6. **Thoroughly Tested**: Tested on 51 cases including different types, edge cases, new custom class, and inappropriate arguments.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install csignum-fast
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ```python
34
+ from signum import sign
35
+ from decimal import Decimal
36
+
37
+ print(sign(-10**100)) # -1
38
+ print(sign(3.14)) # 1
39
+ print(sign(Decimal("0.0"))) # 0
40
+ print(sign(float('-nan'))) # nan
41
+ ```
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.4
2
+ Name: csignum-fast
3
+ Version: 1.0.0
4
+ Summary: High-performance universal sign function
5
+ Author-email: Alexandru Colesnicov <acolesnicov@gmx.com>
6
+ License-Expression: MIT
7
+ Keywords: math,signum,sign,c-extension
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Libraries
11
+ Classifier: Programming Language :: C
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+ # csignum-fast
19
+ ![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)
20
+ ![License](https://img.shields.io/badge/license-MIT-green.svg)
21
+ ![Status](https://img.shields.io/badge/release-Christmas%20Edition-red.svg)
22
+ ![Tests](https://img.shields.io/badge/tests-51%20passed-brightgreen.svg)
23
+
24
+ **A robust, high-performance C++ implementation of the universal sign function for Python.**
25
+ *Released on December 25, 2025 — The Christmas Edition.*
26
+
27
+ ## Key Features
28
+
29
+ 1. **Uniform Results**: Always returns only `-1`, `0`, or `1` as an `int` for valid numeric comparisons.
30
+ 2. **Correct Edge Case Handling**:
31
+ * `sign(+0.0)` and `sign(-0.0)` return `0`.
32
+ * `sign(inf)` returns `1`, `sign(-inf)` returns `-1`.
33
+ * For any **NaN** (float NaN, Decimal NaN, etc.), it returns `math.nan` (float).
34
+ 3. **Comprehensive Duck Typing**: Delegates comparisons to the argument's class. Works seamlessly with:
35
+ * Built-in `int` (including arbitrary-precision), `bool`, and `float`.
36
+ * `fractions.Fraction` and `decimal.Decimal`.
37
+ * Any existing and future objects that support rich comparisons with numbers.
38
+ 4. **Informative Error Handling for Easy Debugging**: Provides clear, descriptive `TypeError` messages when passed non-numeric, non-scalar, or incomparable arguments.
39
+ 5. **High Performance**: Implemented in C++ using a branchless ternary logic approach (no `if-else` chains), ensuring maximum execution speed.
40
+ 6. **Thoroughly Tested**: Tested on 51 cases including different types, edge cases, new custom class, and inappropriate arguments.
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install csignum-fast
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ```python
51
+ from signum import sign
52
+ from decimal import Decimal
53
+
54
+ print(sign(-10**100)) # -1
55
+ print(sign(3.14)) # 1
56
+ print(sign(Decimal("0.0"))) # 0
57
+ print(sign(float('-nan'))) # nan
58
+ ```
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ signum.cpp
6
+ csignum_fast.egg-info/PKG-INFO
7
+ csignum_fast.egg-info/SOURCES.txt
8
+ csignum_fast.egg-info/dependency_links.txt
9
+ csignum_fast.egg-info/top_level.txt
10
+ tests/test_signum.py
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "csignum-fast"
7
+ version = "1.0.0"
8
+ description = "High-performance universal sign function"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ authors = [
13
+ {name = "Alexandru Colesnicov", email = "acolesnicov@gmx.com"}
14
+ ]
15
+ keywords = ["math", "signum", "sign", "c-extension"]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Intended Audience :: Developers",
19
+ "Topic :: Software Development :: Libraries",
20
+ "Programming Language :: C",
21
+ "Programming Language :: Python :: 3.13",
22
+ ]
23
+
24
+ [tool.setuptools]
25
+ ext-modules = [
26
+ {name = "signum", sources = ["signum.cpp"]}
27
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,130 @@
1
+ /*
2
+ * signum.cpp
3
+ * A robust, branchless implementation of the universal sign function for Python.
4
+ * Version: 1.0.0
5
+ * Released: December 25, 2025 (Christmas Edition)
6
+ * Author: Alexandru Colesnicov
7
+ * License: MIT
8
+ */
9
+
10
+ #include <Python.h>
11
+
12
+ static PyObject *
13
+ signum_sign(PyObject *module, PyObject *x)
14
+ {
15
+ /* Check for numeric NaN */
16
+ double d = PyFloat_AsDouble(x);
17
+ if (Py_IS_NAN(d)) return PyFloat_FromDouble(Py_NAN);
18
+ /* If it is something special, we will try comparisons */
19
+ if (PyErr_Occurred()) PyErr_Clear();
20
+
21
+ PyObject *zero = PyLong_FromLong(0);
22
+ if (!zero) return NULL; /* Memory Error? */
23
+
24
+ int gt = PyObject_RichCompareBool(x, zero, Py_GT) + 1; /* 2: True; 1: False; 0: Error */
25
+ int lt = PyObject_RichCompareBool(x, zero, Py_LT) + 1;
26
+ int res = gt - lt; /* Result, if nothing special */
27
+ int eq = PyObject_RichCompareBool(x, zero, Py_EQ) + 1; /* Used only to process NaN and errors */
28
+
29
+ Py_DECREF(zero); /* Not used anymore */
30
+
31
+ /* gt, lt, eq can be 0, 1, 2; let them be digits in the ternary number system */
32
+ /* code = 9*gt+3*lt+eq is the value of the number written in the ternary system
33
+ with these digits, with possible decimal values from 0 to 26;
34
+ 0 means that all three comparisons returned Error (quite possible for inappropriate type);
35
+ 26 means that all were True (extremely strange, the argument is > 0, and < 0, and == 0) */
36
+ /* We also use 9 = 8+1, 3 = 4-1, and replace multiplication by 8 and 4 with shift */
37
+ int code = (gt << 3) + (lt << 2) + eq + res;
38
+ /* (gt<<3) = 8*gt; (lt<<2) = 4*gt; code = 8*gt + 4*lt + eq + (gt-lt) = 9*gt+3*lt+eq */
39
+
40
+ switch (code) {
41
+ case 13: { /* 111₃ -> 8+4+1+0: possible NaN (False, False, False) */
42
+ int self_eq = PyObject_RichCompareBool(x, x, Py_EQ);
43
+ switch (self_eq) {
44
+ case -1: return NULL; /* Error in __eq__, we keep current Python error */
45
+ case 0: return PyFloat_FromDouble(Py_NAN); /* NaN: not equal to itself */
46
+ default: goto error; /* Not a NaN: equals to itself; not comparable to 0 */
47
+ }
48
+ }
49
+ case 14: /* 112₃ -> 8+4+2+0: x == 0 (res= 0) (False, False, True ) */
50
+ case 16: /* 121₃ -> 8+8+1-1: x < 0 (res=-1) (False, True, False) */
51
+ case 22: /* 211₃ -> 16+4+1+1: x > 0 (res= 1) (True, False, False) */
52
+ return PyLong_FromLong((long)res);
53
+ default: /* No more valid cases */
54
+ goto error;
55
+ }
56
+
57
+ error:
58
+ if (PyErr_Occurred()) {
59
+ PyObject *type, *value, *traceback;
60
+ /* Extract the current error */
61
+ PyErr_Fetch(&type, &value, &traceback);
62
+ PyErr_NormalizeException(&type, &value, &traceback);
63
+
64
+ /* Prepare the argument details */
65
+ PyObject *repr = PyObject_Repr(x);
66
+ const char *type_name = Py_TYPE(x)->tp_name;
67
+
68
+ /* Prepare the old error as string */
69
+ PyObject *old_msg = PyObject_Str(value);
70
+ const char *old_msg_str = old_msg ? PyUnicode_AsUTF8(old_msg) : "unknown error";
71
+
72
+ /* Format the new message */
73
+ PyErr_Format(PyExc_TypeError,
74
+ "signum.sign: invalid argument `%.160s` (type '%.80s'). "
75
+ "Inner error: %.320s",
76
+ repr ? PyUnicode_AsUTF8(repr) : "???",
77
+ type_name,
78
+ old_msg_str);
79
+
80
+ /* Clean memory */
81
+ Py_XDECREF(repr);
82
+ Py_XDECREF(old_msg);
83
+ Py_XDECREF(type);
84
+ Py_XDECREF(value);
85
+ Py_XDECREF(traceback);
86
+ }
87
+ else {
88
+ PyObject *repr = PyObject_Repr(x);
89
+ const char *type_name = Py_TYPE(x)->tp_name;
90
+
91
+ if (repr) {
92
+ PyErr_Format(PyExc_TypeError,
93
+ "signum.sign: invalid argument `%.160s`. "
94
+ "Type '%.80s' does not support order comparisons (>, <, ==) "
95
+ "or NaN detection.",
96
+ PyUnicode_AsUTF8(repr),
97
+ type_name);
98
+ Py_DECREF(repr);
99
+ }
100
+ else {
101
+ PyErr_Format(PyExc_TypeError,
102
+ "signum.sign: invalid argument of type '%.80s', "
103
+ "which does not support order comparisons (>, <, ==) and printing.",
104
+ type_name);
105
+ }
106
+ }
107
+ return NULL;
108
+ }
109
+
110
+ /* --- FORMALITIES --- */
111
+
112
+ /* List of implemented methods */
113
+ static PyMethodDef SignumMethods[] = {
114
+ {"sign", (PyCFunction)signum_sign, METH_O, "Return the sign of x: -1, 0, 1, or NaN."},
115
+ {NULL, NULL, 0, NULL} /* Stop-string */
116
+ };
117
+
118
+ /* Module description */
119
+ static struct PyModuleDef signummodule = {
120
+ PyModuleDef_HEAD_INIT,
121
+ "signum", /* Module name for import */
122
+ "Fast signum implementation with ternary logic.",
123
+ -1,
124
+ SignumMethods
125
+ };
126
+
127
+ /* Module initialization */
128
+ PyMODINIT_FUNC PyInit_signum(void) {
129
+ return PyModule_Create(&signummodule);
130
+ }
@@ -0,0 +1,307 @@
1
+ try:
2
+ from math import nan, isnan, inf
3
+ from signum import sign
4
+ import unittest
5
+ except ImportError as e:
6
+ print(e)
7
+ print("To pass these signum tests, you should have 'math', 'signum', and 'unittest' modules installed")
8
+ print("Terminated, no tests passed")
9
+ exit(1)
10
+
11
+ class TestSignum(unittest.TestCase):
12
+
13
+ def trace(self, pcnt, cnt, what):
14
+ delta = cnt - pcnt
15
+ pl = 's' if delta > 1 else ''
16
+ print(f"{delta} test{pl} for {what} passed, total {cnt} tests passed")
17
+
18
+ def test_sign(self):
19
+ counter = 0
20
+ # --- int
21
+ prev_counter = counter
22
+ self.assertEqual(sign(-5), -1); counter += 1
23
+ self.assertEqual(sign(-1), -1); counter += 1
24
+ self.assertEqual(sign(0), 0); counter += 1
25
+ self.assertEqual(sign(1), 1); counter += 1
26
+ self.assertEqual(sign(5), 1); counter += 1
27
+ self.trace(prev_counter, counter, "'int'")
28
+ # ------ bool
29
+ prev_counter = counter
30
+ self.assertEqual(sign(True), 1); counter += 1
31
+ self.assertEqual(sign(False), 0); counter += 1
32
+ self.trace(prev_counter, counter, "'bool'")
33
+ # ------ big numbers
34
+ prev_counter = counter
35
+ self.assertEqual(sign(10**1000), 1); counter += 1
36
+ self.assertEqual(sign(-10**1000), -1); counter += 1
37
+ self.assertEqual(sign(10**1000-10**1000), 0); counter += 1
38
+ self.trace(prev_counter, counter, "big 'int'")
39
+
40
+ # --- float
41
+ prev_counter = counter
42
+ self.assertEqual(sign(-5.0), -1); counter += 1
43
+ self.assertEqual(sign(-1.0), -1); counter += 1
44
+ self.assertEqual(sign(0.0), 0); counter += 1
45
+ self.assertEqual(sign(1.0), 1); counter += 1
46
+ self.assertEqual(sign(5.0), 1); counter += 1
47
+ self.trace(prev_counter, counter, "'float'")
48
+ # ------ -0.0 and +0.0
49
+ prev_counter = counter
50
+ self.assertEqual(sign(float('-0.0')), 0); counter += 1
51
+ self.assertEqual(sign(float('+0.0')), 0); counter += 1
52
+ self.trace(prev_counter, counter, "±0.0")
53
+ # ------ -inf and inf
54
+ prev_counter = counter
55
+ self.assertEqual(sign(-inf), -1); counter += 1
56
+ self.assertEqual(sign(inf), 1); counter += 1
57
+ self.trace(prev_counter, counter, "infinity")
58
+ # ------ -nan (the same as nan), nan
59
+ prev_counter = counter
60
+ self.assertTrue(isnan(sign(float('-nan')))); counter += 1
61
+ self.assertTrue(isnan(sign(nan))); counter += 1
62
+ self.assertTrue(isnan(sign(0.0*nan))); counter += 1
63
+ self.trace(prev_counter, counter, "NaN")
64
+
65
+ # --- Fraction
66
+ try:
67
+ from fractions import Fraction
68
+ have_fractions = True
69
+ except ImportError as e:
70
+ have_fractions = False
71
+ print(e)
72
+ print("No 'fractions' module found in your installation. Tests for 'Fraction' are skipped")
73
+ if have_fractions:
74
+ prev_counter = counter
75
+ self.assertEqual(sign(Fraction(-5, 2)), -1); counter += 1
76
+ self.assertEqual(sign(Fraction(-1, 2)), -1); counter += 1
77
+ self.assertEqual(sign(Fraction(0, 2)), 0); counter += 1
78
+ self.assertEqual(sign(Fraction(1, 2)), 1); counter += 1
79
+ self.assertEqual(sign(Fraction(5, 2)), 1); counter += 1
80
+ self.trace(prev_counter, counter, "'Fraction'")
81
+
82
+ # --- Decimal
83
+ try:
84
+ from decimal import Decimal
85
+ have_decimal = True
86
+ except ImportError as e:
87
+ have_decimal = False
88
+ print(e)
89
+ print("No 'decimal' module found in your installation. Tests for 'Decimal' are skipped")
90
+ if have_decimal:
91
+ prev_counter = counter
92
+ self.assertEqual(sign(Decimal(-5.5)), -1); counter += 1
93
+ self.assertEqual(sign(Decimal(-1.5)), -1); counter += 1
94
+ self.assertEqual(sign(Decimal(0.0)), 0); counter += 1
95
+ self.assertEqual(sign(Decimal(1.5)), 1); counter += 1
96
+ self.assertEqual(sign(Decimal(5.5)), 1); counter += 1
97
+ self.trace(prev_counter, counter, "'Decimal'")
98
+ # ------ Decimal NaN
99
+ prev_counter = counter
100
+ self.assertTrue(isnan(sign(Decimal('NaN')))); counter += 1
101
+ self.trace(prev_counter, counter, "Decimal NaN")
102
+
103
+ # --- sympy
104
+ try:
105
+ import sympy
106
+ have_sympy = True
107
+ except ImportError as e:
108
+ have_sympy = False
109
+ print(e)
110
+ print("No 'sympy' module found in your installation. Tests for 'sympy' are skipped")
111
+ if have_sympy:
112
+ x_sym = sympy.Symbol('x')
113
+ expr = x_sym
114
+ val = expr.subs(x_sym, -3.14)
115
+ prev_counter = counter
116
+ self.assertEqual(sign(val), -1); counter += 1
117
+ self.assertEqual(sign(sympy.Rational(3, 4)), 1); counter += 1
118
+ self.trace(prev_counter, counter, "sympy")
119
+ # ------ sympy.nan
120
+ prev_counter = counter
121
+ self.assertTrue(isnan(sign(sympy.nan))); counter += 1
122
+ self.trace(prev_counter, counter, "sympy.nan")
123
+
124
+ # --- New custom class (testing possible future extentions)
125
+ # This class has no __float__ that tests one subtle branch in the C++ code
126
+ class MyNumber:
127
+ def __init__(self, value):
128
+ self.value = value
129
+ def __gt__(self, other):
130
+ return self.value > other
131
+ def __lt__(self, other):
132
+ return self.value < other
133
+ def __eq__(self, other):
134
+ return self.value == other
135
+ def __repr__(self):
136
+ return f'MyNumber({self.value})'
137
+
138
+ prev_counter = counter
139
+ self.assertEqual(sign(MyNumber(-5)), -1); counter += 1
140
+ self.assertEqual(sign(MyNumber(-1)), -1); counter += 1
141
+ self.assertEqual(sign(MyNumber(0)), 0); counter += 1
142
+ self.assertEqual(sign(MyNumber(1)), 1); counter += 1
143
+ self.assertEqual(sign(MyNumber(5)), 1); counter += 1
144
+ with self.assertRaisesRegex(TypeError, r'signum\.sign: invalid argument `MyNumber\(nan\)`'):
145
+ sign(MyNumber(nan))
146
+ counter += 1
147
+ self.trace(prev_counter, counter, "new custom class")
148
+
149
+ # Testing inappropriate arguments and types (non-scalar, non-comparable, etc.)
150
+ # --- No arguments and three arguments
151
+ prev_counter = counter
152
+ with self.assertRaisesRegex(TypeError, r'signum\.sign\(\) takes exactly one argument \(0 given\)'):
153
+ sign()
154
+ counter += 1
155
+ with self.assertRaisesRegex(TypeError, r'signum\.sign\(\) takes exactly one argument \(3 given\)'):
156
+ sign(-1, 0, 1)
157
+ counter += 1
158
+ self.trace(prev_counter, counter, "invalid number of arguments")
159
+
160
+ # --- None, str, list, complex, set
161
+ tests = [(r"`None`", None),
162
+ (r"`'5\.0'`", '5.0'),
163
+ (r"`'nan'`", 'nan'),
164
+ (r"`'number 5'`", 'number 5'),
165
+ (r"`\[-8\.75\]`", [-8.75]),
166
+ (r"`\(-1\+1j\)`", -1+1j),
167
+ (r"`\{-3\.14\}`", {-3.14}),
168
+ ]
169
+
170
+ prev_counter = counter
171
+ for msg, obj in tests:
172
+ with self.subTest(obj=obj):
173
+ with self.assertRaisesRegex(TypeError,
174
+ r'signum\.sign: invalid argument ' + msg):
175
+ sign(obj)
176
+ counter += 1
177
+ self.trace(prev_counter, counter, "inappropriate types")
178
+
179
+ print(f'Success, {counter} tests passed.')
180
+
181
+ # print('--- int')
182
+ # print("sign(-5):", sign(-5))
183
+ # print("sign(-1):", sign(-1))
184
+ # print("sign(0):", sign(0))
185
+ # print("sign(1):", sign(1))
186
+ # print("sign(5):", sign(5))
187
+ #
188
+ # print('------ bool')
189
+ # print("sign(True):", sign(True))
190
+ # print("sign(False):", sign(False))
191
+ #
192
+ # print('------ big numbers')
193
+ # print('sign(10**1000):', sign(10**1000))
194
+ # print('sign(-10**1000):', sign(-10**1000))
195
+ # print('sign(10**1000-10**1000):', sign(10**1000-10**1000))
196
+ #
197
+ # print('\n--- float')
198
+ # print("sign(-5.0):", sign(-5.0))
199
+ # print("sign(-1.0):", sign(-1.0))
200
+ # print("sign(0.0):", sign(0.0))
201
+ # print("sign(1.0):", sign(1.0))
202
+ # print("sign(5.0):", sign(5.0))
203
+ # print('------ -0.0 and +0.0')
204
+ # print("sign(float('-0.0')):", sign(float('-0.0')))
205
+ # print("sign(float('+0.0')):", sign(float('+0.0')))
206
+ # print('------ -inf and inf')
207
+ # print("sign(-inf):", sign(-inf))
208
+ # print("sign(inf):", sign(inf))
209
+ # print('------ -nan and nan')
210
+ # print("sign(float('-nan')):", sign(float('-nan')))
211
+ # print("sign(nan):", sign(nan))
212
+ # print("sign(0.0*nan):", sign(0.0*nan))
213
+ #
214
+ # print('\n--- Fraction')
215
+ # print("sign(Fraction(-5, 2)):", sign(Fraction(-5, 2)))
216
+ # print("sign(Fraction(-1, 2)):", sign(Fraction(-1, 2)))
217
+ # print("sign(Fraction(0, 2)):", sign(Fraction(0, 2)))
218
+ # print("sign(Fraction(1, 2)):", sign(Fraction(1, 2)))
219
+ # print("sign(Fraction(5, 2)):", sign(Fraction(5, 2)))
220
+ #
221
+ # print('\n--- Decimal')
222
+ # print("sign(Decimal(-5.5)):", sign(Decimal(-5.5)))
223
+ # print("sign(Decimal(-1.5)):", sign(Decimal(-1.5)))
224
+ # print("sign(Decimal(0.0)):", sign(Decimal(0.0)))
225
+ # print("sign(Decimal(1.5)):", sign(Decimal(1.5)))
226
+ # print("sign(Decimal(5.5)):", sign(Decimal(5.5)))
227
+ #
228
+ # print("------ Decimal('NaN')")
229
+ # print("sign(Decimal('NaN')):", sign(Decimal('NaN')))
230
+ #
231
+ # print('\n--- sympy (substitution and Rational)')
232
+ # x_sym = sympy.Symbol('x')
233
+ # expr = x_sym
234
+ # val = expr.subs(x_sym, -3.14)
235
+ # print(f"Type of val is {type(val)}")
236
+ # print(f"Type of (val > 0) is {type(val > 0)}")
237
+ # print("sign(val):", sign(val))
238
+ # print("sign(sympy.Rational(3, 4)):", sign(sympy.Rational(3, 4)))
239
+ #
240
+ # print('------ sympy.nan')
241
+ # print("sign(sympy.nan):", sign(sympy.nan))
242
+ #
243
+ # print('\n--- My Custom Class That Have >, <, == With Numbers But Nothing Else')
244
+ # print("sign(MyNumber(-5)):", sign(MyNumber(-5)))
245
+ # print("sign(MyNumber(-1)):", sign(MyNumber(-1)))
246
+ # print("sign(MyNumber(0)):", sign(MyNumber(0)))
247
+ # print("sign(MyNumber(1)):", sign(MyNumber(1)))
248
+ # print("sign(MyNumber(5.1)):", sign(MyNumber(5.1)))
249
+ # try:
250
+ # print("sign(MyNumber(nan)):", sign(MyNumber(nan)))
251
+ # except TypeError as e:
252
+ # print(e)
253
+ #
254
+ # print('\n--- No arguments')
255
+ # try:
256
+ # print("sign():", sign())
257
+ # except TypeError as e:
258
+ # print(e)
259
+ #
260
+ # print('\n--- Three arguments')
261
+ # try:
262
+ # print("sign(-1, 0, 1):", sign(-1, 0, 1))
263
+ # except TypeError as e:
264
+ # print(e)
265
+ #
266
+ # print('\n--- None')
267
+ # try:
268
+ # print("sign(None):", sign(None))
269
+ # except TypeError as e:
270
+ # print(e)
271
+ #
272
+ # print('\n--- str')
273
+ # try:
274
+ # print("sign('5.0'):", sign('5.0'))
275
+ # except TypeError as e:
276
+ # print(e)
277
+ #
278
+ # try:
279
+ # print("sign('nan'):", sign('nan'))
280
+ # except TypeError as e:
281
+ # print(e)
282
+ #
283
+ # try:
284
+ # print("sign('number 5'):", sign('number 5'))
285
+ # except TypeError as e:
286
+ # print(e)
287
+ #
288
+ # print('\n--- list')
289
+ # try:
290
+ # print("sign([-8.75]):", sign([-8.75]))
291
+ # except TypeError as e:
292
+ # print(e)
293
+ #
294
+ # print('\n--- complex')
295
+ # try:
296
+ # print("sign(-1+1j):", sign(-1+1j))
297
+ # except TypeError as e:
298
+ # print(e)
299
+ #
300
+ # print('\n--- set')
301
+ # try:
302
+ # print("sign({-3.14}):", sign({-3.14}))
303
+ # except TypeError as e:
304
+ # print(e)
305
+
306
+ if __name__ == '__main__':
307
+ unittest.main()