math-engine 0.4.0__tar.gz → 0.5.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: math-engine
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: A fast and secure mathematical expression evaluator.
5
5
  Author-email: Jan Teske <jan.teske.06@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/JanTeske06/math_engine
@@ -14,7 +14,7 @@ Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
 
16
16
 
17
- # Math Engine 0.4.0
17
+ # Math Engine 0.5.0
18
18
 
19
19
  [![PyPI Version](https://img.shields.io/pypi/v/math-engine.svg)](https://pypi.org/project/math-engine/)
20
20
  [![License: MIT](https://img.shields.io/pypi/l/math-engine.svg)](https://opensource.org/licenses/MIT)
@@ -34,7 +34,7 @@ It provides a complete pipeline:
34
34
  * Scientific functions
35
35
  * Strict error codes for reliable debugging and automated testing
36
36
 
37
- **Version 0.4.0** introduces a powerful **Command Line Interface (CLI)**
37
+
38
38
 
39
39
  This library is ideal for:
40
40
 
@@ -263,7 +263,7 @@ Non-decimal parsing respects the setting `allow_non_decimal`. If it is set to `F
263
263
 
264
264
  -----
265
265
 
266
- # Bitwise Operations & Developer Mode (v0.4.0)
266
+ # Bitwise Operations & Developer Mode (v0.5.0)
267
267
 
268
268
  Math Engine can act as a **programmer's calculator**. It supports standard operator precedence and bitwise logic.
269
269
 
@@ -326,9 +326,38 @@ math_engine.load_preset(settings)
326
326
  math_engine.evaluate("FF + 3")
327
327
  # Decimal('258')
328
328
  ```
329
-
330
329
  Input validation ensures safety and prevents mixing incompatible formats in strict modes.
331
330
 
331
+ ---
332
+ ## Bitwise & Low-Level Operations
333
+
334
+ Math Engine now includes a rich collection of low-level bit manipulation functions commonly used in systems programming, embedded development, cryptography, and hardware-oriented tools.
335
+
336
+ **Bitwise functions:**
337
+ - `bitand(x, y)` — bitwise AND
338
+ - `bitor(x, y)` — bitwise OR
339
+ - `bitxor(x, y)` — bitwise XOR
340
+ - `bitnot(x)` — bitwise NOT
341
+
342
+ **Bit manipulation utilities:**
343
+ - `setbit(x, n)` — sets bit *n*
344
+ - `clrbit(x, n)` — clears bit *n*
345
+ - `togbit(x, n)` — toggles bit *n*
346
+ - `testbit(x, n)` — returns 1 if bit *n* is set, else 0
347
+
348
+ **Shift operations:**
349
+ - `shl(x, n)` — logical left shift
350
+ - `shr(x, n)` — logical right shift
351
+
352
+ All bitwise functions:
353
+ - respect `word_size` and `signed_mode`
354
+ - support overflow/wrap-around behavior
355
+ - fully support binary, hex, decimal, and octal inputs
356
+ - participate in the AST just like standard operators
357
+ - support underscores in non-decimal literals (`0b1111_0000`)
358
+
359
+ This makes Math Engine behave like a full-featured programmer’s calculator with CPU-like precision control.
360
+
332
361
  -----
333
362
 
334
363
  # Settings System
@@ -1,5 +1,5 @@
1
1
 
2
- # Math Engine 0.4.0
2
+ # Math Engine 0.5.0
3
3
 
4
4
  [![PyPI Version](https://img.shields.io/pypi/v/math-engine.svg)](https://pypi.org/project/math-engine/)
5
5
  [![License: MIT](https://img.shields.io/pypi/l/math-engine.svg)](https://opensource.org/licenses/MIT)
@@ -19,7 +19,7 @@ It provides a complete pipeline:
19
19
  * Scientific functions
20
20
  * Strict error codes for reliable debugging and automated testing
21
21
 
22
- **Version 0.4.0** introduces a powerful **Command Line Interface (CLI)**
22
+
23
23
 
24
24
  This library is ideal for:
25
25
 
@@ -248,7 +248,7 @@ Non-decimal parsing respects the setting `allow_non_decimal`. If it is set to `F
248
248
 
249
249
  -----
250
250
 
251
- # Bitwise Operations & Developer Mode (v0.4.0)
251
+ # Bitwise Operations & Developer Mode (v0.5.0)
252
252
 
253
253
  Math Engine can act as a **programmer's calculator**. It supports standard operator precedence and bitwise logic.
254
254
 
@@ -311,9 +311,38 @@ math_engine.load_preset(settings)
311
311
  math_engine.evaluate("FF + 3")
312
312
  # Decimal('258')
313
313
  ```
314
-
315
314
  Input validation ensures safety and prevents mixing incompatible formats in strict modes.
316
315
 
316
+ ---
317
+ ## Bitwise & Low-Level Operations
318
+
319
+ Math Engine now includes a rich collection of low-level bit manipulation functions commonly used in systems programming, embedded development, cryptography, and hardware-oriented tools.
320
+
321
+ **Bitwise functions:**
322
+ - `bitand(x, y)` — bitwise AND
323
+ - `bitor(x, y)` — bitwise OR
324
+ - `bitxor(x, y)` — bitwise XOR
325
+ - `bitnot(x)` — bitwise NOT
326
+
327
+ **Bit manipulation utilities:**
328
+ - `setbit(x, n)` — sets bit *n*
329
+ - `clrbit(x, n)` — clears bit *n*
330
+ - `togbit(x, n)` — toggles bit *n*
331
+ - `testbit(x, n)` — returns 1 if bit *n* is set, else 0
332
+
333
+ **Shift operations:**
334
+ - `shl(x, n)` — logical left shift
335
+ - `shr(x, n)` — logical right shift
336
+
337
+ All bitwise functions:
338
+ - respect `word_size` and `signed_mode`
339
+ - support overflow/wrap-around behavior
340
+ - fully support binary, hex, decimal, and octal inputs
341
+ - participate in the AST just like standard operators
342
+ - support underscores in non-decimal literals (`0b1111_0000`)
343
+
344
+ This makes Math Engine behave like a full-featured programmer’s calculator with CPU-like precision control.
345
+
317
346
  -----
318
347
 
319
348
  # Settings System
@@ -0,0 +1,184 @@
1
+ from decimal import Decimal, getcontext, Overflow
2
+ from . import error as E
3
+
4
+ # -----------------------------
5
+ # AST node types
6
+ # -----------------------------
7
+
8
+ class Number:
9
+ """AST node for numeric literal backed by Decimal."""
10
+
11
+ def __init__(self, value):
12
+ # Always normalize input to Decimal via string to avoid float artifacts
13
+ if not isinstance(value, Decimal):
14
+ value = str(value)
15
+ self.value = Decimal(value)
16
+
17
+ def evaluate(self):
18
+ """Return Decimal value for this literal."""
19
+ return self.value
20
+
21
+ def collect_term(self, var_name):
22
+ """Return (factor_of_var, constant) for linear collection."""
23
+ return (0, self.value)
24
+
25
+ def __repr__(self):
26
+ # Helpful for debugging/printing the AST
27
+ try:
28
+ display_value = self.value.to_normal_string()
29
+ except AttributeError:
30
+ # Fallback for older Decimal versions
31
+ display_value = str(self.value)
32
+ return f"Number({display_value})"
33
+
34
+
35
+ class Variable:
36
+ """AST node representing a single symbolic variable (e.g. 'var0')."""
37
+
38
+ def __init__(self, name):
39
+ self.name = name
40
+
41
+ def evaluate(self):
42
+ """Variables cannot be directly evaluated without solving."""
43
+ raise E.SolverError(f"Non linear problem.", code="3005")
44
+
45
+ def collect_term(self, var_name):
46
+ """Return (1, 0) if this variable matches var_name; else error."""
47
+ if self.name == var_name:
48
+ return (1, 0)
49
+ else:
50
+ # Only one variable supported in the linear solver
51
+ raise E.SolverError(f"Multiple variables found: {self.name}", code="3002")
52
+ return (0, 0)
53
+
54
+ def __repr__(self):
55
+ return f"Variable('{self.name}')"
56
+
57
+
58
+ class BinOp:
59
+ """AST node for a binary operation: left <operator> right."""
60
+
61
+ def __init__(self, left, operator, right):
62
+ self.left = left
63
+ self.operator = operator
64
+ self.right = right
65
+
66
+ def evaluate(self):
67
+ """Evaluate numeric subtree and apply the binary operator."""
68
+ left_value = self.left.evaluate()
69
+ right_value = self.right.evaluate()
70
+
71
+ if self.operator == '+':
72
+ return left_value + right_value
73
+
74
+ elif self.operator == '-':
75
+ return left_value - right_value
76
+
77
+ elif self.operator == '&':
78
+ if left_value % 1 != 0 or right_value % 1 != 0:
79
+ raise E.CalculationError("Bitwise AND requires integers.", code="3042")
80
+ return Decimal(int(left_value) & int(right_value))
81
+
82
+ elif self.operator == '|':
83
+ if left_value % 1 != 0 or right_value % 1 != 0:
84
+ raise E.CalculationError("Bitwise OR requires integers.", code="3042")
85
+ return Decimal(int(left_value) | int(right_value))
86
+
87
+ elif self.operator == '^':
88
+ if left_value % 1 != 0 or right_value % 1 != 0:
89
+ raise E.CalculationError("XOR requires integers.", code="3042")
90
+ return Decimal(int(left_value) ^ int(right_value))
91
+
92
+ elif self.operator == '<<':
93
+ if left_value % 1 != 0 or right_value % 1 != 0:
94
+ raise E.CalculationError("Bitshift requires integers.", code="3041")
95
+ return Decimal(int(left_value) << int(right_value))
96
+
97
+ elif self.operator == '>>':
98
+ if left_value % 1 != 0 or right_value % 1 != 0:
99
+ raise E.CalculationError("Bitshift requires integers.", code="3041")
100
+ return Decimal(int(left_value) >> int(right_value))
101
+
102
+ elif self.operator == '*':
103
+ return left_value * right_value
104
+
105
+ elif self.operator == '**':
106
+ return left_value ** right_value
107
+
108
+ elif self.operator == '/':
109
+ if right_value == 0:
110
+ raise E.CalculationError("Division by zero", code="3003")
111
+ return left_value / right_value
112
+
113
+ elif self.operator == '=':
114
+ # Equality is evaluated to a boolean (used for "= True/False" responses)
115
+ return left_value == right_value
116
+ else:
117
+ raise E.CalculationError(f"Unknown operator: {self.operator}", code="3004")
118
+
119
+ def collect_term(self, var_name):
120
+ """Collect linear terms on this subtree into (factor_of_var, constant).
121
+
122
+ Only linear combinations are allowed; non-linear forms raise Solver/Syntax errors.
123
+ """
124
+ (left_factor, left_constant) = self.left.collect_term(var_name)
125
+ (right_factor, right_constant) = self.right.collect_term(var_name)
126
+
127
+ if self.operator == '+':
128
+ result_factor = left_factor + right_factor
129
+ result_constant = left_constant + right_constant
130
+ return (result_factor, result_constant)
131
+
132
+ elif self.operator == '-':
133
+ result_factor = left_factor - right_factor
134
+ result_constant = left_constant - right_constant
135
+ return (result_factor, result_constant)
136
+
137
+ elif self.operator == '*':
138
+ # Only constant * (A*x + B) is allowed. (A*x + B)*(C*x + D) would be non-linear.
139
+ if left_factor != 0 and right_factor != 0:
140
+ raise E.SyntaxError("x^x Error.", code="3005")
141
+
142
+ elif left_factor == 0:
143
+ # B * (C*x + D) = (B*C)*x + (B*D)
144
+ result_factor = left_constant * right_factor
145
+ result_constant = left_constant * right_constant
146
+ return (result_factor, result_constant)
147
+
148
+ elif right_factor == 0:
149
+ # (A*x + B) * D = (A*D)*x + (B*D)
150
+ result_factor = right_constant * left_factor
151
+ result_constant = right_constant * left_constant
152
+ return (result_factor, result_constant)
153
+
154
+ elif left_factor == 0 and right_factor == 0:
155
+ # Pure constant multiplication
156
+ result_factor = 0
157
+ result_constant = right_constant * left_constant
158
+ return (result_factor, result_constant)
159
+
160
+ elif self.operator == '/':
161
+ # (A*x + B) / D is allowed; division by (C*x + D) is non-linear
162
+ if right_factor != 0:
163
+ raise E.SolverError("Non-linear equation. (Division by x)", code="3006")
164
+ elif right_constant == 0:
165
+ raise E.SolverError("Solver: Division by zero", code="3003")
166
+ else:
167
+ # (A*x + B) / D = (A/D)*x + (B/D)
168
+ result_factor = left_factor / right_constant
169
+ result_constant = left_constant / right_constant
170
+ return (result_factor, result_constant)
171
+
172
+ elif self.operator == '**':
173
+ # Powers generate non-linear terms (e.g., x^2)
174
+ raise E.SolverError("Powers are not supported by the linear solver.", code="3007")
175
+
176
+ elif self.operator == '=':
177
+ # '=' only belongs at the root for solving; not inside collection
178
+ raise E.SolverError("Should not happen: '=' inside collect_terms", code="3720")
179
+
180
+ else:
181
+ raise E.CalculationError(f"Unknown operator: {self.operator}", code="3004")
182
+
183
+ def __repr__(self):
184
+ return f"BinOp({self.operator!r}, left={self.left}, right={self.right})"
@@ -8,7 +8,7 @@ from . import error as E
8
8
  from typing import Any, Mapping, Optional
9
9
  from typing import Union
10
10
  from typing import Any, Mapping
11
- __version__ = "0.4.0"
11
+ __version__ = "0.5.0"
12
12
  memory = {}
13
13
 
14
14
  def set_memory(key_value: str, value:str):
@@ -119,7 +119,10 @@ def validate(expr: str,
119
119
  def reset_settings():
120
120
  config_manager.reset_settings()
121
121
  #
122
- if __name__ == '__main__':
123
- #problem = ("x+1", x=5)
124
- print(math_engine.evaluate("x += 5", x=10))
125
- config_manager.reset_settings()
122
+ # if __name__ == '__main__':
123
+ # #problem = ("x+1", x=5)
124
+ # print(math_engine.evaluate("bitxor(shr(shl(bitnot(7), 2), 4), 3) + 100"))
125
+ # print(evaluate("int:(bitand(0b1101,0b1011)+ bitor(0b0011,0b0101)+ bitxor(0xF0,0b1010)+ shl(3,4)+ shr(0b100000,3)+ setbit(0b0001,2)+ clrbit(0b1111,1)+ togbit(0b1010,1))"))
126
+ # print(evaluate("bool:testbit(0b1010, 3)"))
127
+ # #print(math_engine.evaluate("bitor(bitand(0b101110110, bitnot(4096)),160) * 3"))
128
+ # config_manager.reset_settings()