mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
add cmath
module
This commit is contained in:
parent
b89395a1fe
commit
1e178caf6e
12
docs/modules/cmath.md
Normal file
12
docs/modules/cmath.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
icon: package
|
||||||
|
label: cmath
|
||||||
|
---
|
||||||
|
|
||||||
|
!!!
|
||||||
|
This module is experimental and may have bugs or other issues.
|
||||||
|
!!!
|
||||||
|
|
||||||
|
Mathematical functions for complex numbers.
|
||||||
|
|
||||||
|
https://docs.python.org/3/library/cmath.html
|
@ -85,6 +85,7 @@ class Compiler {
|
|||||||
|
|
||||||
void exprLiteral();
|
void exprLiteral();
|
||||||
void exprLong();
|
void exprLong();
|
||||||
|
void exprImag();
|
||||||
void exprBytes();
|
void exprBytes();
|
||||||
void exprFString();
|
void exprFString();
|
||||||
void exprLambda();
|
void exprLambda();
|
||||||
|
@ -133,6 +133,12 @@ struct BytesExpr: Expr{
|
|||||||
void emit_(CodeEmitContext* ctx) override;
|
void emit_(CodeEmitContext* ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ImagExpr: Expr{
|
||||||
|
f64 value;
|
||||||
|
ImagExpr(f64 value): value(value) {}
|
||||||
|
void emit_(CodeEmitContext* ctx) override;
|
||||||
|
};
|
||||||
|
|
||||||
// @num, @str which needs to invoke OP_LOAD_CONST
|
// @num, @str which needs to invoke OP_LOAD_CONST
|
||||||
struct LiteralExpr: Expr{
|
struct LiteralExpr: Expr{
|
||||||
TokenValue value;
|
TokenValue value;
|
||||||
|
@ -11,7 +11,7 @@ typedef uint8_t TokenIndex;
|
|||||||
constexpr const char* kTokens[] = {
|
constexpr const char* kTokens[] = {
|
||||||
"is not", "not in", "yield from",
|
"is not", "not in", "yield from",
|
||||||
"@eof", "@eol", "@sof",
|
"@eof", "@eol", "@sof",
|
||||||
"@id", "@num", "@str", "@fstr", "@long", "@bytes",
|
"@id", "@num", "@str", "@fstr", "@long", "@bytes", "@imag",
|
||||||
"@indent", "@dedent",
|
"@indent", "@dedent",
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
"+", "+=", "-", "-=", // (INPLACE_OP - 1) can get '=' removed
|
"+", "+=", "-", "-=", // (INPLACE_OP - 1) can get '=' removed
|
||||||
|
@ -40,6 +40,7 @@ OPCODE(DELETE_ATTR)
|
|||||||
OPCODE(DELETE_SUBSCR)
|
OPCODE(DELETE_SUBSCR)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(BUILD_LONG)
|
OPCODE(BUILD_LONG)
|
||||||
|
OPCODE(BUILD_IMAG)
|
||||||
OPCODE(BUILD_BYTES)
|
OPCODE(BUILD_BYTES)
|
||||||
OPCODE(BUILD_TUPLE)
|
OPCODE(BUILD_TUPLE)
|
||||||
OPCODE(BUILD_LIST)
|
OPCODE(BUILD_LIST)
|
||||||
|
@ -225,6 +225,8 @@ const StrName __class__ = StrName::get("__class__");
|
|||||||
|
|
||||||
const StrName pk_id_add = StrName::get("add");
|
const StrName pk_id_add = StrName::get("add");
|
||||||
const StrName pk_id_set = StrName::get("set");
|
const StrName pk_id_set = StrName::get("set");
|
||||||
|
const StrName pk_id_long = StrName::get("long");
|
||||||
|
const StrName pk_id_complex = StrName::get("complex");
|
||||||
|
|
||||||
#define DEF_SNAME(name) const static StrName name(#name)
|
#define DEF_SNAME(name) const static StrName name(#name)
|
||||||
|
|
||||||
|
@ -279,4 +279,11 @@ class classmethod:
|
|||||||
def staticmethod(f):
|
def staticmethod(f):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
from _long import long
|
|
||||||
|
def complex(*args, **kwargs):
|
||||||
|
import cmath
|
||||||
|
return cmath.complex(*args, **kwargs)
|
||||||
|
|
||||||
|
def long(*args, **kwargs):
|
||||||
|
import _long
|
||||||
|
return _long.long(*args, **kwargs)
|
||||||
|
158
python/cmath.py
Normal file
158
python/cmath.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
class complex:
|
||||||
|
def __init__(self, real, imag=0):
|
||||||
|
self._real = float(real)
|
||||||
|
self._imag = float(imag)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def real(self):
|
||||||
|
return self._real
|
||||||
|
|
||||||
|
@property
|
||||||
|
def imag(self):
|
||||||
|
return self._imag
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"({self.real}+{self.imag}j)"
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if type(other) is complex:
|
||||||
|
return self.real == other.real and self.imag == other.imag
|
||||||
|
if type(other) in (int, float):
|
||||||
|
return self.real == other and self.imag == 0
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if type(other) is complex:
|
||||||
|
return complex(self.real + other.real, self.imag + other.imag)
|
||||||
|
if type(other) in (int, float):
|
||||||
|
return complex(self.real + other, self.imag)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return self.__add__(other)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
if type(other) is complex:
|
||||||
|
return complex(self.real - other.real, self.imag - other.imag)
|
||||||
|
if type(other) in (int, float):
|
||||||
|
return complex(self.real - other, self.imag)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
if type(other) is complex:
|
||||||
|
return complex(other.real - self.real, other.imag - self.imag)
|
||||||
|
if type(other) in (int, float):
|
||||||
|
return complex(other - self.real, -self.imag)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
if type(other) is complex:
|
||||||
|
return complex(self.real * other.real - self.imag * other.imag,
|
||||||
|
self.real * other.imag + self.imag * other.real)
|
||||||
|
if type(other) in (int, float):
|
||||||
|
return complex(self.real * other, self.imag * other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
return self.__mul__(other)
|
||||||
|
|
||||||
|
def __pow__(self, other: int | float):
|
||||||
|
if type(other) in (int, float):
|
||||||
|
return complex(self.__abs__() ** other * math.cos(other * phase(self)),
|
||||||
|
self.__abs__() ** other * math.sin(other * phase(self)))
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __abs__(self) -> float:
|
||||||
|
return math.sqrt(self.real ** 2 + self.imag ** 2)
|
||||||
|
|
||||||
|
|
||||||
|
# Conversions to and from polar coordinates
|
||||||
|
|
||||||
|
def phase(z: complex):
|
||||||
|
return math.atan2(z.imag, z.real)
|
||||||
|
|
||||||
|
def polar(z: complex):
|
||||||
|
return z.__abs__(), phase(z)
|
||||||
|
|
||||||
|
def rect(r: float, phi: float):
|
||||||
|
return r * math.cos(phi) + r * math.sin(phi) * 1j
|
||||||
|
|
||||||
|
# Power and logarithmic functions
|
||||||
|
|
||||||
|
def exp(z: complex):
|
||||||
|
return math.exp(z.real) * rect(1, z.imag)
|
||||||
|
|
||||||
|
def log(z: complex, base=2.718281828459045):
|
||||||
|
return math.log(z.__abs__(), base) + phase(z) * 1j
|
||||||
|
|
||||||
|
def log10(z: complex):
|
||||||
|
return log(z, 10)
|
||||||
|
|
||||||
|
def sqrt(z: complex):
|
||||||
|
return z ** 0.5
|
||||||
|
|
||||||
|
# Trigonometric functions
|
||||||
|
|
||||||
|
def acos(z: complex):
|
||||||
|
return -1j * log(z + sqrt(z * z - 1))
|
||||||
|
|
||||||
|
def asin(z: complex):
|
||||||
|
return -1j * log(1j * z + sqrt(1 - z * z))
|
||||||
|
|
||||||
|
def atan(z: complex):
|
||||||
|
return 1j / 2 * log((1 - 1j * z) / (1 + 1j * z))
|
||||||
|
|
||||||
|
def cos(z: complex):
|
||||||
|
return (exp(z) + exp(-z)) / 2
|
||||||
|
|
||||||
|
def sin(z: complex):
|
||||||
|
return (exp(z) - exp(-z)) / (2 * 1j)
|
||||||
|
|
||||||
|
def tan(z: complex):
|
||||||
|
return sin(z) / cos(z)
|
||||||
|
|
||||||
|
# Hyperbolic functions
|
||||||
|
|
||||||
|
def acosh(z: complex):
|
||||||
|
return log(z + sqrt(z * z - 1))
|
||||||
|
|
||||||
|
def asinh(z: complex):
|
||||||
|
return log(z + sqrt(z * z + 1))
|
||||||
|
|
||||||
|
def atanh(z: complex):
|
||||||
|
return 1 / 2 * log((1 + z) / (1 - z))
|
||||||
|
|
||||||
|
def cosh(z: complex):
|
||||||
|
return (exp(z) + exp(-z)) / 2
|
||||||
|
|
||||||
|
def sinh(z: complex):
|
||||||
|
return (exp(z) - exp(-z)) / 2
|
||||||
|
|
||||||
|
def tanh(z: complex):
|
||||||
|
return sinh(z) / cosh(z)
|
||||||
|
|
||||||
|
# Classification functions
|
||||||
|
|
||||||
|
def isfinite(z: complex):
|
||||||
|
return math.isfinite(z.real) and math.isfinite(z.imag)
|
||||||
|
|
||||||
|
def isinf(z: complex):
|
||||||
|
return math.isinf(z.real) or math.isinf(z.imag)
|
||||||
|
|
||||||
|
def isnan(z: complex):
|
||||||
|
return math.isnan(z.real) or math.isnan(z.imag)
|
||||||
|
|
||||||
|
def isclose(*args, **kwargs):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
|
||||||
|
pi = math.pi
|
||||||
|
e = math.e
|
||||||
|
tau = 2 * pi
|
||||||
|
inf = math.inf
|
||||||
|
infj = complex(0, inf)
|
||||||
|
nan = math.nan
|
||||||
|
nanj = complex(0, nan)
|
@ -282,11 +282,15 @@ __NEXT_STEP:;
|
|||||||
}DISPATCH();
|
}DISPATCH();
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(BUILD_LONG) {
|
TARGET(BUILD_LONG) {
|
||||||
PK_LOCAL_STATIC const StrName m_long("long");
|
PyObject* _0 = builtins->attr().try_get_likely_found(pk_id_long);
|
||||||
PyObject* _0 = builtins->attr().try_get_likely_found(m_long);
|
if(_0 == nullptr) AttributeError(builtins, pk_id_long);
|
||||||
if(_0 == nullptr) AttributeError(builtins, m_long);
|
|
||||||
TOP() = call(_0, TOP());
|
TOP() = call(_0, TOP());
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
|
TARGET(BUILD_IMAG) {
|
||||||
|
PyObject* _0 = builtins->attr().try_get_likely_found(pk_id_complex);
|
||||||
|
if(_0 == nullptr) AttributeError(builtins, pk_id_long);
|
||||||
|
TOP() = call(_0, VAR(0), TOP());
|
||||||
|
} DISPATCH();
|
||||||
TARGET(BUILD_BYTES) {
|
TARGET(BUILD_BYTES) {
|
||||||
const Str& s = CAST(Str&, TOP());
|
const Str& s = CAST(Str&, TOP());
|
||||||
unsigned char* p = new unsigned char[s.size];
|
unsigned char* p = new unsigned char[s.size];
|
||||||
@ -832,8 +836,8 @@ __NEXT_STEP:;
|
|||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
|
|
||||||
#if !PK_ENABLE_COMPUTED_GOTO
|
#if !PK_ENABLE_COMPUTED_GOTO
|
||||||
static_assert(OP_DEC_GLOBAL == 110);
|
static_assert(OP_DEC_GLOBAL == 111);
|
||||||
case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254: case 255: FATAL_ERROR(); break;
|
case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254: case 255: FATAL_ERROR(); break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,7 @@ namespace pkpy{
|
|||||||
rules[TK("@str")] = { PK_METHOD(exprLiteral), PK_NO_INFIX };
|
rules[TK("@str")] = { PK_METHOD(exprLiteral), PK_NO_INFIX };
|
||||||
rules[TK("@fstr")] = { PK_METHOD(exprFString), PK_NO_INFIX };
|
rules[TK("@fstr")] = { PK_METHOD(exprFString), PK_NO_INFIX };
|
||||||
rules[TK("@long")] = { PK_METHOD(exprLong), PK_NO_INFIX };
|
rules[TK("@long")] = { PK_METHOD(exprLong), PK_NO_INFIX };
|
||||||
|
rules[TK("@imag")] = { PK_METHOD(exprImag), PK_NO_INFIX };
|
||||||
rules[TK("@bytes")] = { PK_METHOD(exprBytes), PK_NO_INFIX };
|
rules[TK("@bytes")] = { PK_METHOD(exprBytes), PK_NO_INFIX };
|
||||||
#undef PK_METHOD
|
#undef PK_METHOD
|
||||||
#undef PK_NO_INFIX
|
#undef PK_NO_INFIX
|
||||||
@ -198,6 +199,10 @@ namespace pkpy{
|
|||||||
ctx()->s_expr.push(make_expr<LongExpr>(prev().str()));
|
ctx()->s_expr.push(make_expr<LongExpr>(prev().str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Compiler::exprImag(){
|
||||||
|
ctx()->s_expr.push(make_expr<ImagExpr>(std::get<f64>(prev().value)));
|
||||||
|
}
|
||||||
|
|
||||||
void Compiler::exprBytes(){
|
void Compiler::exprBytes(){
|
||||||
ctx()->s_expr.push(make_expr<BytesExpr>(std::get<Str>(prev().value)));
|
ctx()->s_expr.push(make_expr<BytesExpr>(std::get<Str>(prev().value)));
|
||||||
}
|
}
|
||||||
|
@ -216,6 +216,12 @@ namespace pkpy{
|
|||||||
ctx->emit_(OP_BUILD_LONG, BC_NOARG, line);
|
ctx->emit_(OP_BUILD_LONG, BC_NOARG, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImagExpr::emit_(CodeEmitContext* ctx) {
|
||||||
|
VM* vm = ctx->vm;
|
||||||
|
ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(value)), line);
|
||||||
|
ctx->emit_(OP_BUILD_IMAG, BC_NOARG, line);
|
||||||
|
}
|
||||||
|
|
||||||
void BytesExpr::emit_(CodeEmitContext* ctx) {
|
void BytesExpr::emit_(CodeEmitContext* ctx) {
|
||||||
VM* vm = ctx->vm;
|
VM* vm = ctx->vm;
|
||||||
ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(s)), line);
|
ctx->emit_(OP_LOAD_CONST, ctx->add_const(VAR(s)), line);
|
||||||
|
@ -13,7 +13,7 @@ std::set<char> kValidChars = {
|
|||||||
// A-Z
|
// A-Z
|
||||||
'A','B','C','D','E','F',
|
'A','B','C','D','E','F',
|
||||||
// other valid chars
|
// other valid chars
|
||||||
'.', 'L', 'x', 'b', 'o',
|
'.', 'L', 'x', 'b', 'o', 'j'
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool is_unicode_Lo_char(uint32_t c) {
|
static bool is_unicode_Lo_char(uint32_t c) {
|
||||||
@ -304,6 +304,11 @@ static bool is_unicode_Lo_char(uint32_t c) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(i[-1] == 'j' && p_end == text.data() + text.size() - 1){
|
||||||
|
add_token(TK("@imag"), (f64)float_out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SyntaxError("invalid number literal");
|
SyntaxError("invalid number literal");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1660,7 +1660,7 @@ void VM::post_init(){
|
|||||||
add_module_operator(this);
|
add_module_operator(this);
|
||||||
add_module_csv(this);
|
add_module_csv(this);
|
||||||
|
|
||||||
for(const char* name: {"this", "functools", "heapq", "bisect", "pickle", "_long", "colorsys", "typing", "datetime", "dataclasses"}){
|
for(const char* name: {"this", "functools", "heapq", "bisect", "pickle", "_long", "colorsys", "typing", "datetime", "dataclasses", "cmath"}){
|
||||||
_lazy_modules[name] = kPythonLibs[name];
|
_lazy_modules[name] = kPythonLibs[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
tests/10_cmath.py
Normal file
17
tests/10_cmath.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
assert 1+2j == complex(1, 2) == 2j+1
|
||||||
|
|
||||||
|
assert 1+2j + 3+4j == 4+6j
|
||||||
|
|
||||||
|
assert 1+2j - 3+4j == -2+6j
|
||||||
|
|
||||||
|
assert (1+2j).real == 1
|
||||||
|
assert (1+2j).imag == 2
|
||||||
|
|
||||||
|
assert (1+2j)*(3+4j) == -5+10j
|
||||||
|
assert (1+2j)*3 == 3+6j
|
||||||
|
|
||||||
|
import cmath
|
||||||
|
|
||||||
|
res = cmath.sqrt(1+2j)
|
||||||
|
assert round(res.real, 3) == 1.272, res.real
|
||||||
|
assert round(res.imag, 3) == 0.786, res.imag
|
Loading…
x
Reference in New Issue
Block a user