sku-pg
Version:
Skulpt is a Javascript implementation of Python 2.x. Python that runs in your browser!
1,190 lines (952 loc) • 38 kB
JavaScript
/**
* hypot is a ESCMA6 function and maybe not available across all browsers
*/
Math.hypot = Math.hypot || function() {
var y = 0;
var length = arguments.length;
for (var i = 0; i < length; i++) {
if (arguments[i] === Infinity || arguments[i] === -Infinity) {
return Infinity;
}
y += arguments[i] * arguments[i];
}
return Math.sqrt(y);
};
/**
* complex_new see https://hg.python.org/cpython/file/f0e2caad4200/Objects/complexobject.c#l911
* @constructor
* @param {Object} real part of the complex number
* @param {?Object=} imag part of the complex number
* @this {Sk.builtin.object}
*
* Prefering here == instead of ===, otherwise also undefined has to be matched explicitly
*
* FIXME: it seems that we somehow need to call __float__/__int__ if arguments provide those methods
*
*/
Sk.builtin.complex = function (real, imag) {
Sk.builtin.pyCheckArgs("complex", arguments, 0, 2);
var r, i, tmp; // PyObject
var nbr, nbi; // real, imag as numbers
var own_r = false;
var cr = {}; // PyComplexObject
var ci = {}; // PyComplexObject
var cr_is_complex = false;
var ci_is_complex = false;
// not sure why this is required
if (!(this instanceof Sk.builtin.complex)) {
return new Sk.builtin.complex(real, imag);
}
// check if kwargs
// ToDo: this is only a temporary replacement
r = real == null ? Sk.builtin.bool.false$ : real; // r = Py_False;
i = imag;
// handle case if passed in arguments are of type complex
if (r instanceof Sk.builtin.complex && i == null) {
return real;
}
if (r != null && Sk.builtin.checkString(r)) {
if(i != null) {
throw new Sk.builtin.TypeError("complex() can't take second arg if first is a string");
}
return Sk.builtin.complex.complex_subtype_from_string(r);
}
if (i != null && Sk.builtin.checkString(i)) {
throw new Sk.builtin.TypeError("complex() second arg can't be a string");
}
// try_complex_special_method
tmp = Sk.builtin.complex.try_complex_special_method(r);
if (tmp != null && tmp !== Sk.builtin.NotImplemented.NotImplemented$) {
if (!Sk.builtin.checkComplex(tmp)) {
throw new Sk.builtin.TypeError("__complex__ should return a complex object");
}
r = tmp;
}
// this check either returns a javascript number or the passed object
// but it actually, should check for r->ob_type->tp_as_number
// this check is useless
nbr = Sk.builtin.asnum$(r);
if (i != null) {
nbi = Sk.builtin.asnum$(i);
}
// this function mimics the tp_as_number->nb_float check in cpython
var nb_float = function(op) {
if(Sk.builtin.checkNumber(op)) {
return true;
}
if(Sk.builtin.type.typeLookup(op.ob$type, "__float__") !== undefined) {
return true;
}
};
// check for valid arguments
if (nbr == null || (!nb_float(r) && !Sk.builtin.checkComplex(r)) || ((i != null) && (nbi == null || (!nb_float(i) && !Sk.builtin.checkComplex(i))))) {
throw new Sk.builtin.TypeError("complex() argument must be a string or number");
}
/* If we get this far, then the "real" and "imag" parts should
both be treated as numbers, and the constructor should return a
complex number equal to (real + imag*1j).
Note that we do NOT assume the input to already be in canonical
form; the "real" and "imag" parts might themselves be complex
numbers, which slightly complicates the code below. */
if (Sk.builtin.complex._complex_check(r)) {
/* Note that if r is of a complex subtype, we're only
retaining its real & imag parts here, and the return
value is (properly) of the builtin complex type. */
cr.real = r.real.v;
cr.imag = r.imag.v;
cr_is_complex = true;
} else {
/* The "real" part really is entirely real, and contributes
nothing in the imaginary direction.
Just treat it as a double. */
tmp = Sk.builtin.float_.PyFloat_AsDouble(r); // tmp = PyNumber_Float(r);
if (tmp == null) {
return null;
}
cr.real = tmp;
cr.imag = 0.0;
}
if (i == null) {
ci.real = 0.0;
} else if (Sk.builtin.complex._complex_check(i)) {
ci.real = i.real.v;
ci.imag = i.imag.v;
ci_is_complex = true;
} else {
/* The "imag" part really is entirely imaginary, and
contributes nothing in the real direction.
Just treat it as a double. */
tmp = Sk.builtin.float_.PyFloat_AsDouble(i);
if (tmp == null) {
return null;
}
ci.real = tmp;
ci.imag = 0.0;
}
/* If the input was in canonical form, then the "real" and "imag"
parts are real numbers, so that ci.imag and cr.imag are zero.
We need this correction in case they were not real numbers. */
if (ci_is_complex === true) {
cr.real -= ci.imag;
}
if (cr_is_complex === true) {
ci.real += cr.imag;
}
// adjust for negated imaginary literal
if (cr.real === 0 && (ci.real < 0 || Sk.builtin.complex._isNegativeZero(ci.real))) {
cr.real = -0;
}
// save them as properties
this.real = new Sk.builtin.float_(cr.real);
this.imag = new Sk.builtin.float_(ci.real);
this.__class__ = Sk.builtin.complex;
return this;
};
Sk.abstr.setUpInheritance("complex", Sk.builtin.complex, Sk.builtin.numtype);
//Sk.builtin.complex.co_kwargs = true;
Sk.builtin.complex.prototype.nb$int_ = function () {
throw new Sk.builtin.TypeError("can't convert complex to int");
};
Sk.builtin.complex.prototype.nb$float_ = function() {
throw new Sk.builtin.TypeError("can't convert complex to float");
};
Sk.builtin.complex.prototype.nb$lng = function () {
throw new Sk.builtin.TypeError("can't convert complex to long");
};
Sk.builtin.complex.prototype.__doc__ = new Sk.builtin.str("complex(real[, imag]) -> complex number\n\nCreate a complex number from a real part and an optional imaginary part.\nThis is equivalent to (real + imag*1j) where imag defaults to 0.");
Sk.builtin.complex._isNegativeZero = function (val) {
if (val !== 0) {
return false;
}
return 1/val === -Infinity;
};
/**
* Internal method to check if op has __complex__
*/
Sk.builtin.complex.try_complex_special_method = function (op) {
var complexstr = new Sk.builtin.str("__complex__");
var f; // PyObject
var res;
// return early
if (op == null) {
return null;
}
// the lookup special method does already all the magic
f = Sk.abstr.lookupSpecial(op, "__complex__");
if (f != null) {
// method on builtin, provide this arg
res = Sk.misceval.callsim(f, op);
return res;
}
return null;
};
/**
Check if given argument is number or complex and always
returns complex type.
*/
Sk.builtin.complex.check_number_or_complex = function (other) {
/* exit early */
if (!Sk.builtin.checkNumber(other) && other.tp$name !== "complex") {
throw new Sk.builtin.TypeError("unsupported operand type(s) for +: 'complex' and '" + Sk.abstr.typeName(other) + "'");
}
/* converting to complex allows us to use always only one formula */
if (Sk.builtin.checkNumber(other)) {
other = new Sk.builtin.complex(other); // create complex
}
return other;
};
/**
Parses a string repr of a complex number
*/
Sk.builtin.complex.complex_subtype_from_string = function (val) {
var index;
var start;
var val_wws; // val with removed beginning ws and (
var x = 0.0, y = 0.0; // real, imag parts
var got_bracket = false; // flag for braces
var len; // total length of val
var match; // regex result
// first check if val is javascript string or python string
if (Sk.builtin.checkString(val)) {
val = Sk.ffi.remapToJs(val);
} else if (typeof val !== "string") {
throw new TypeError("provided unsupported string-alike argument");
}
/* This is an python specific error, this does not do any harm in js, but we want
* to be as close to the orginial impl. as possible.
*
* Check also for empty strings. They are not allowed.
*/
if (val.indexOf("\0") !== -1 || val.length === 0 || val === "") {
throw new Sk.builtin.ValueError("complex() arg is a malformed string");
}
// transform to unicode
// ToDo: do we need this?
index = 0; // first char
// do some replacements for javascript floats
val = val.replace(/inf|infinity/gi, "Infinity");
val = val.replace(/nan/gi, "NaN");
/* position on first nonblank */
start = 0;
while (val[index] === " ") {
index++;
}
if (val[index] === "(") {
/* skip over possible bracket from repr(). */
got_bracket = true;
index++;
while (val[index] === " ") {
index++;
}
}
/* a valid complex string usually takes one of the three forms:
<float> - real part only
<float>j - imaginary part only
<float><signed-float>j - real and imaginary parts
where <float> represents any numeric string that's accepted by the
float constructor (including 'nan', 'inf', 'infinity', etc.), and
<signed-float> is any string of the form <float> whose first character
is '+' or '-'.
For backwards compatibility, the extra forms
<float><sign>j
<sign>j
j
are also accepted, though support for these forms my be removed from
a future version of Python.
* This is a complete regular expression for matching any valid python floats, e.g.:
* - 1.0
* - 0.
* - .1
* - nan/inf/infinity
* - +-1.0
* - +3.E-3
*
* In order to work, this pattern requires only lower case characters
* There is case insensitive group option in js.
*
* the [eE] could be refactored to soley e
*/
var float_regex2 = /^(?:[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[eE][+-]?\d+)?|NaN|Infinity))/;
val_wws = val.substr(index); // val with removed whitespace and "("
/* first try to match a float at the beginning */
match = val_wws.match(float_regex2);
if (match !== null) {
// one of the first 4 cases
index += match[0].length;
/* <float>j */
if (val[index] === "j" || val[index] === "J") {
y = parseFloat(match[0]);
index++;
} else if(val[index] === "+" || val[index] === "-") {
/* <float><signed-float>j | <float><sign>j */
x = parseFloat(match[0]);
match = val.substr(index).match(float_regex2);
if (match !== null) {
/* <float><signed-float>j */
y = parseFloat(match[0]);
index += match[0].length;
} else {
/* <float><sign>j */
y = val[index] === "+" ? 1.0 : -1.0;
index++;
}
if (val[index] !== "j" && val[index] !== "J") {
throw new Sk.builtin.ValueError("complex() arg is malformed string");
}
index++;
} else {
/* <float> */
x = parseFloat(match[0]);
}
} else {
// maybe <sign>j or j
match = match = val_wws.match(/^([+-]?[jJ])/);
if (match !== null) {
if (match[0].length === 1) {
y = 1.0; // must be j
} else {
y = match[0][0] === "+" ? 1.0 : -1.0;
}
index += match[0].length;
}
}
while (val[index] === " ") {
index++;
}
if (got_bracket) {
/* if there was an opening parenthesis, then the corresponding
closing parenthesis should be right here */
if (val[index] !== ")") {
throw new Sk.builtin.ValueError("complex() arg is malformed string");
}
index++;
while (val[index] === " ") {
index++;
}
}
/* we should now be at the end of the string */
if (val.length !== index) {
throw new Sk.builtin.ValueError("complex() arg is malformed string");
}
// return here complex number parts
return new Sk.builtin.complex(new Sk.builtin.float_(x), new Sk.builtin.float_(y));
};
/**
_PyHASH_IMAG refers to _PyHASH_MULTIPLIER which refers to 1000003
*/
Sk.builtin.complex.prototype.tp$hash = function () {
return new Sk.builtin.int_(this.tp$getattr("imag").v * 1000003 + this.tp$getattr("real").v);
};
Sk.builtin.complex.prototype.nb$add = function (other) {
var real;
var imag;
other = Sk.builtin.complex.check_number_or_complex(other);
real = this.tp$getattr("real").v + other.tp$getattr("real").v;
imag = this.tp$getattr("imag").v + other.tp$getattr("imag").v;
return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag));
};
/* internal subtract/diff function that calls internal float diff */
Sk.builtin.complex._c_diff = function (a, b) {
var r, i; // Py_Float
r = a.real.nb$subtract.call(a.real, b.real);
i = a.imag.nb$subtract.call(a.imag, b.imag);
return new Sk.builtin.complex(r, i);
};
Sk.builtin.complex.prototype.nb$subtract = function (other) {
var result; // Py_complex
var a, b; // Py_complex
a = Sk.builtin.complex.check_number_or_complex(this);
b = Sk.builtin.complex.check_number_or_complex(other);
result = Sk.builtin.complex._c_diff(a, b);
return result;
};
Sk.builtin.complex.prototype.nb$multiply = function (other) {
var real;
var imag;
var a, b; // Py_complex
a = this;
b = Sk.builtin.complex.check_number_or_complex(other);
real = a.real.v * b.real.v - a.imag.v * b.imag.v;
imag = a.real.v * b.imag.v + a.imag.v * b.real.v;
return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag));
};
/**
* Otherwise google closure complains about ZeroDivisionError not being
* defined
* @suppress {missingProperties}
*
* implementation based on complexobject.c:c_quot
*/
Sk.builtin.complex.prototype.nb$divide = function (other) {
var real;
var imag;
other = Sk.builtin.complex.check_number_or_complex(other);
var ratio;
var denom;
// other == b
var breal = other.real.v;
var bimag = other.imag.v;
// this == a
var areal = this.real.v;
var aimag = this.imag.v;
var abs_breal = Math.abs(breal);
var abs_bimag = Math.abs(bimag);
if (abs_breal >= abs_bimag) {
// divide tops and bottom by breal
if (abs_breal === 0.0) {
throw new Sk.builtin.ZeroDivisionError("complex division by zero");
} else {
ratio = bimag / breal;
denom = breal + bimag * ratio;
real = (areal + aimag * ratio) / denom;
imag = (aimag - areal * ratio) / denom;
}
} else if (abs_bimag >= abs_breal) {
// divide tops and bottom by b.imag
ratio = breal / bimag;
denom = breal * ratio + bimag;
goog.asserts.assert(bimag !== 0.0);
real = (areal * ratio + aimag) / denom;
imag = (aimag * ratio - areal) / denom;
} else {
// At least one of b.real or b.imag is a NaN
real = NaN;
imag = NaN;
}
return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag));
};
Sk.builtin.complex.prototype.nb$floor_divide = function (other) {
throw new Sk.builtin.TypeError("can't take floor of complex number.");
};
Sk.builtin.complex.prototype.nb$remainder = function (other) {
throw new Sk.builtin.TypeError("can't mod complex numbers.");
};
/**
* @param {?Object=} z, modulo operation
*/
Sk.builtin.complex.prototype.nb$power = function (other, z) {
var p;
var exponent;
var int_exponent;
var a, b;
// none is allowed
if (z != null && !Sk.builtin.checkNone(z)) {
throw new Sk.builtin.ValueError("complex modulo");
}
a = this;
b = Sk.builtin.complex.check_number_or_complex(other);
exponent = b;
int_exponent = b.real.v | 0; // js convert to int
if (exponent.imag.v === 0.0 && exponent.real.v === int_exponent) {
p = Sk.builtin.complex.c_powi(a, int_exponent);
} else {
p = Sk.builtin.complex.c_pow(a, exponent);
}
return p;
};
// power of complex a and complex exponent b
Sk.builtin.complex.c_pow = function (a, b) {
var real, imag; // Py_complex
var vabs;
var len;
var at;
var phase;
// other == b
var breal = b.real.v;
var bimag = b.imag.v;
// this == a
var areal = a.real.v;
var aimag = a.imag.v;
if (breal === 0.0 && bimag === 0.0) {
real = 1.0;
imag = 0.0;
} else if (areal === 0.0 && aimag === 0.0) {
if(bimag !== 0.0 || breal < 0.0) {
throw new Sk.builtin.ZeroDivisionError("complex division by zero");
}
real = 0.0;
imag = 0.0;
} else {
vabs = Math.hypot(areal, aimag);
len = Math.pow(vabs, breal);
at = Math.atan2(aimag, areal);
phase = at * breal;
if(bimag !== 0.0) {
len /= Math.exp(at * bimag);
phase += bimag * Math.log(vabs);
}
real = len * Math.cos(phase);
imag = len * Math.sin(phase);
}
return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag));
};
// power of complex x and integer exponent n
Sk.builtin.complex.c_powi = function (x, n) {
var cn; // Py_complex
var c1;
if (n > 100 || n < -100) {
cn = new Sk.builtin.complex(new Sk.builtin.float_(n), new Sk.builtin.float_(0.0));
return Sk.builtin.complex.c_pow(x, cn);
} else if (n > 0) {
return Sk.builtin.complex.c_powu(x, n);
} else {
// return c_quot(c_1,c_powu(x,-n));
c1 = new Sk.builtin.complex(new Sk.builtin.float_(1.0), new Sk.builtin.float_(0.0));
return c1.nb$divide(Sk.builtin.complex.c_powu(x,-n));
}
};
Sk.builtin.complex.c_powu = function (x, n) {
var r, p; // Py_complex
var mask = 1;
r = new Sk.builtin.complex(new Sk.builtin.float_(1.0), new Sk.builtin.float_(0.0));
p = x;
while (mask > 0 && n >= mask) {
if (n & mask) {
r = r.nb$multiply(p);
}
mask <<= 1;
p = p.nb$multiply(p);
}
return r;
};
Sk.builtin.complex.prototype.nb$inplace_add = Sk.builtin.complex.prototype.nb$add;
Sk.builtin.complex.prototype.nb$inplace_subtract = Sk.builtin.complex.prototype.nb$subtract;
Sk.builtin.complex.prototype.nb$inplace_multiply = Sk.builtin.complex.prototype.nb$multiply;
Sk.builtin.complex.prototype.nb$inplace_divide = Sk.builtin.complex.prototype.nb$divide;
Sk.builtin.complex.prototype.nb$inplace_remainder = Sk.builtin.complex.prototype.nb$remainder;
Sk.builtin.complex.prototype.nb$inplace_floor_divide = Sk.builtin.complex.prototype.nb$floor_divide;
Sk.builtin.complex.prototype.nb$inplace_power = Sk.builtin.complex.prototype.nb$power;
Sk.builtin.complex.prototype.nb$negative = function () {
var real;
var imag;
// this == a
var areal = this.real.v;
var aimag = this.imag.v;
real = -areal;
imag = -aimag;
return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag));
};
Sk.builtin.complex.prototype.nb$positive = function () {
return Sk.builtin.complex.check_number_or_complex(this);
};
/**
* check if op is instance of complex or a sub-type
*/
Sk.builtin.complex._complex_check = function (op) {
if (op === undefined) {
return false;
}
if (op instanceof Sk.builtin.complex || (op.tp$name && op.tp$name === "complex")) {
return true;
}
// check if type of ob is a subclass
if (Sk.builtin.issubclass(new Sk.builtin.type(op), Sk.builtin.complex)) {
return true;
}
return false;
};
Sk.builtin.complex.prototype.tp$richcompare = function (w, op) {
var result;
var equal;
var i;
if (op !== "Eq" && op !== "NotEq") {
if(Sk.builtin.checkNumber(w) || Sk.builtin.complex._complex_check(w)) {
throw new Sk.builtin.TypeError("no ordering relation is defined for complex numbers");
}
return Sk.builtin.NotImplemented.NotImplemented$;
}
// assert(PyComplex_Check(v)));
i = Sk.builtin.complex.check_number_or_complex(this);
var _real = i.tp$getattr("real").v;
var _imag = i.tp$getattr("imag").v;
if (Sk.builtin.checkInt(w)) {
/* Check for 0.0 imaginary part first to avoid the rich
* comparison when possible.
*/
// if true, the complex number has just a real part
if (_imag === 0.0) {
equal = Sk.misceval.richCompareBool(new Sk.builtin.float_(_real), w, op);
result = new Sk.builtin.bool( equal);
return result;
} else {
equal = false;
}
} else if (Sk.builtin.checkFloat(w)) {
equal = (_real === Sk.builtin.float_.PyFloat_AsDouble(w) && _imag === 0.0);
} else if (Sk.builtin.complex._complex_check(w)) {
// ToDo: figure if we need to call to_complex
var w_real = w.tp$getattr("real").v;
var w_imag = w.tp$getattr("imag").v;
equal = _real === w_real && _imag === w_imag;
} else {
return Sk.builtin.NotImplemented.NotImplemented$;
}
// invert result if op == NotEq
if(op === "NotEq") {
equal = !equal;
}
// wrap as bool
result = new Sk.builtin.bool( equal);
return result;
};
// Despite what jshint may want us to do, these two functions need to remain
// as == and != Unless you modify the logic of numberCompare do not change
// these.
Sk.builtin.complex.prototype.__eq__ = function (me, other) {
return Sk.builtin.complex.prototype.tp$richcompare.call(me, other, "Eq");
};
Sk.builtin.complex.prototype.__ne__ = function (me, other) {
return Sk.builtin.complex.prototype.tp$richcompare.call(me, other, "NotEq");
};
/**
* Do we really need to implement those? Otherwise I can't find in Sk.abstr a place where this particular
* expcetion is thrown.git co
*/
Sk.builtin.complex.prototype.__lt__ = function (me, other) {
throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " < " + Sk.abstr.typeName(other));
};
Sk.builtin.complex.prototype.__le__ = function (me, other) {
throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " <= " + Sk.abstr.typeName(other));
};
Sk.builtin.complex.prototype.__gt__ = function (me, other) {
throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " > " + Sk.abstr.typeName(other));
};
Sk.builtin.complex.prototype.__ge__ = function (me, other) {
throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " >= " + Sk.abstr.typeName(other));
};
Sk.builtin.complex.prototype.__float__ = function (self) {
throw new Sk.builtin.TypeError("can't convert complex to float");
};
Sk.builtin.complex.prototype.__int__ = function (self) {
throw new Sk.builtin.TypeError("can't convert complex to int");
};
Sk.builtin.complex.prototype._internalGenericGetAttr = Sk.builtin.object.prototype.GenericGetAttr;
/**
* Custom getattr impl. to get the c.real and c.imag to work. Though we should
* consider to implement tp$members that always are attributs on the class and
* will be used in the genericgetattr method.
* Would be super easy to implement the readonly stuff too.
*
*/
Sk.builtin.complex.prototype.tp$getattr = function (name) {
if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) {
var _name = name;
// get javascript string
if (Sk.builtin.checkString(name)) {
_name = Sk.ffi.remapToJs(name);
}
if (_name === "real" || _name === "imag") {
return this[_name];
}
}
// if we have not returned yet, try the genericgetattr
return this._internalGenericGetAttr(name);
};
Sk.builtin.complex.prototype.tp$setattr = function (name, value) {
if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) {
var _name = name;
// get javascript string
if (Sk.builtin.checkString(name)) {
_name = Sk.ffi.remapToJs(name);
}
if (_name === "real" || _name === "imag") {
throw new Sk.builtin.AttributeError("readonly attribute");
}
}
// builtin: --> all is readonly (I am not happy with this)
throw new Sk.builtin.AttributeError("'complex' object attribute '" + name + "' is readonly");
};
/**
* Internal format function for repr and str
* It is not intended for __format__ calls
*
* This functions assumes, that v is always instance of Sk.builtin.complex
*/
Sk.builtin.complex.complex_format = function (v, precision, format_code){
function copysign (a, b) {
return b < 0 ? -Math.abs(a) : Math.abs(a);
}
if (v == null || !Sk.builtin.complex._complex_check(v)) {
throw new Error("Invalid internal method call: Sk.complex.complex_format() called with invalid value type.");
}
var result; // PyObject
var pre = "";
var im = "";
var re = null;
var lead = "";
var tail = "";
if (v.real.v === 0.0 && copysign(1.0, v.real.v) == 1.0) {
re = "";
im = Sk.builtin.complex.PyOS_double_to_string(v.imag.v, format_code, precision, 0, null);
// im = v.imag.v;
} else {
/* Format imaginary part with sign, real part without */
pre = Sk.builtin.complex.PyOS_double_to_string(v.real.v, format_code, precision, 0, null);
re = pre;
im = Sk.builtin.complex.PyOS_double_to_string(v.imag.v, format_code, precision, Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN, null);
if (v.imag.v === 0 && 1/v.imag.v === -Infinity && im && im[0] !== "-"){
im = "-" + im; // force negative zero sign
}
lead = "(";
tail = ")";
}
result = "" + lead + re + im + "j" + tail; // concat all parts
return new Sk.builtin.str(result);
};
Sk.builtin.complex.prototype["$r"] = function () {
return Sk.builtin.complex.complex_format(this, 0, "r");
};
Sk.builtin.complex.prototype.tp$str = function () {
return Sk.builtin.complex.complex_format(this, null, "g"); // g, 12 == Py_Float_STR_PRECISION
};
/**
* https://hg.python.org/cpython/file/3cf2990d19ab/Objects/complexobject.c#l907
* also see _PyComplex_FormatAdvanced
*
* We currently use the signature (self, format_spec) instead of (self, args). So we do
* not need to unwrap the args.
*/
Sk.builtin.complex.prototype.__format__ = new Sk.builtin.func(function (self, format_spec){
var result; // PyObject
if (format_spec == null) {
return null;
}
if (Sk.builtin.checkString(format_spec)) {
result = Sk.builtin.complex._PyComplex_FormatAdvanced(self, format_spec);
return result;
}
throw new Sk.builtin.TypeError("__format__ requires str or unicode");
});
Sk.builtin.complex._PyComplex_FormatAdvanced = function(self, format_spec) {
throw new Sk.builtin.NotImplementedError("__format__ is not implemented for complex type.");
};
/**
Return true if float or double are is neither infinite nor NAN, else false
Value is already a Javascript object
*/
Sk.builtin.complex._is_finite = function (val) {
return !isNaN(val) && val !== Infinity && val !== -Infinity;
};
Sk.builtin.complex._is_infinity = function (val) {
return val === Infinity || val === -Infinity;
};
/**
* @suppress {missingProperties}
*/
Sk.builtin.complex.prototype.__abs__ = new Sk.builtin.func(function (self) {
var result;
var _real = self.real.v;
var _imag = self.imag.v;
if (!Sk.builtin.complex._is_finite(_real) || !Sk.builtin.complex._is_finite(_imag)) {
/* C99 rules: if either the real or the imaginary part is an
infinity, return infinity, even if the other part is a
NaN.
*/
if (Sk.builtin.complex._is_infinity(_real)) {
result = Math.abs(_real);
return new Sk.builtin.float_(result);
}
if (Sk.builtin.complex._is_infinity(_imag)) {
result = Math.abs(_imag);
return new Sk.builtin.float_(result);
}
/* either the real or imaginary part is a NaN,
and neither is infinite. Result should be NaN. */
return new Sk.builtin.float_(NaN);
}
result = Math.hypot(_real, _imag);
if (!Sk.builtin.complex._is_finite(result)) {
throw new Sk.builtin.OverflowError("absolute value too large");
}
return new Sk.builtin.float_(result);
});
Sk.builtin.complex.prototype.__bool__ = new Sk.builtin.func(function (self) {
return new Sk.builtin.bool( self.tp$getattr("real").v || self.tp$getattr("real").v);
});
Sk.builtin.complex.prototype.__truediv__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__truediv__", arguments, 1, 1, true);
return self.nb$divide.call(self, other);
});
Sk.builtin.complex.prototype.__hash__ = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("__hash__", arguments, 0, 0, true);
return self.tp$hash.call(self);
});
Sk.builtin.complex.prototype.__add__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__add__", arguments, 1, 1, true);
return self.nb$add.call(self, other);
});
Sk.builtin.complex.prototype.__repr__ = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("__repr__", arguments, 0, 0, true);
return self["r$"].call(self);
});
Sk.builtin.complex.prototype.__str__ = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("__str__", arguments, 0, 0, true);
return self.tp$str.call(self);
});
Sk.builtin.complex.prototype.__sub__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__sub__", arguments, 1, 1, true);
return self.nb$subtract.call(self, other);
});
Sk.builtin.complex.prototype.__mul__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__mul__", arguments, 1, 1, true);
return self.nb$multiply.call(self, other);
});
Sk.builtin.complex.prototype.__div__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__div__", arguments, 1, 1, true);
return self.nb$divide.call(self, other);
});
Sk.builtin.complex.prototype.__floordiv__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__floordiv__", arguments, 1, 1, true);
return self.nb$floor_divide.call(self, other);
});
Sk.builtin.complex.prototype.__mod__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__mod__", arguments, 1, 1, true);
return self.nb$remainder.call(self, other);
});
Sk.builtin.complex.prototype.__pow__ = new Sk.builtin.func(function (self, other, z){
Sk.builtin.pyCheckArgs("__pow__", arguments, 1, 2, true);
return self.nb$power.call(self, other, z);
});
Sk.builtin.complex.prototype.__neg__ = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("__neg__", arguments, 0, 0, true);
return self.nb$negative.call(self);
});
Sk.builtin.complex.prototype.__pos__ = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("__pos__", arguments, 0, 0, true);
return self.nb$positive.call(self);
});
Sk.builtin.complex.prototype.conjugate = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("conjugate", arguments, 0, 0, true);
var _imag = self.imag.v;
_imag = -_imag;
return new Sk.builtin.complex(self.real, new Sk.builtin.float_(_imag));
});
// deprecated
Sk.builtin.complex.prototype.__divmod__ = new Sk.builtin.func(function (self, other){
Sk.builtin.pyCheckArgs("__divmod__", arguments, 1, 1, true);
var div, mod; // Py_complex
var d, m, z; // PyObject
var a, b; // Py_complex
a = Sk.builtin.complex.check_number_or_complex(self);
b = Sk.builtin.complex.check_number_or_complex(other);
div = a.nb$divide.call(a, b); // the raw divisor value
div.real = new Sk.builtin.float_(Math.floor(div.real.v));
div.imag = new Sk.builtin.float_(0.0);
mod = a.nb$subtract.call(a, b.nb$multiply.call(b, div));
z = new Sk.builtin.tuple([div, mod]);
return z;
});
Sk.builtin.complex.prototype.__getnewargs__ = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("__getnewargs__", arguments, 0, 0, true);
return new Sk.builtin.tuple([self.real, self.imag]);
});
Sk.builtin.complex.prototype.__nonzero__ = new Sk.builtin.func(function (self){
Sk.builtin.pyCheckArgs("__nonzero__", arguments, 0, 0, true);
if(self.real.v !== 0.0 || self.imag.v !== 0.0) {
return Sk.builtin.bool.true$;
} else {
return Sk.builtin.bool.false$;
}
});
// ToDo: think about inplace methods too
goog.exportSymbol("Sk.builtin.complex", Sk.builtin.complex);
/**
* Convert a double val to a string using supplied format_code, precision, and flags.
*
* format_code must be one of 'e', 'E', 'f', 'F', 'g', 'G' or 'r'. For 'r', the supplied precision must be 0 and is ignored. The 'r' format code specifies the standard repr() format.
*
* flags can be zero or more of the values Py_DTSF_SIGN, Py_DTSF_ADD_DOT_0, or Py_DTSF_ALT, or-ed together:
*
* Py_DTSF_SIGN means to always precede the returned string with a sign character, even if val is non-negative.
* Py_DTSF_ADD_DOT_0 means to ensure that the returned string will not look like an integer.
* Py_DTSF_ALT means to apply “alternate” formatting rules. See the documentation for the PyOS_snprintf() '#' specifier for details.
* If ptype is non-NULL, then the value it points to will be set to one of Py_DTST_FINITE, Py_DTST_INFINITE, or Py_DTST_NAN, signifying that val is a finite number, an
* infinite number, or not a number, respectively.
*/
Sk.builtin.complex.PyOS_double_to_string = function(val, format_code, precision, flags, type) {
var format;
var buf;
var t;
var exp;
var upper = false;
// Validate format code, and map upper and lower case
switch(format_code) {
case "e": /* exponent */
case "f": /* fixed */
case "g": /* general */
break;
case "E":
upper = true;
format_code = "e";
break;
case "F":
upper = true;
format_code = "f";
break;
case "r": /* repr format */
// Supplied precision is unused, must be 0.
if(precision !== 0) {
throw new Error("Bad internall call"); // only happens when somebody messes up calling this in js
}
// repr() precision is 17 significant decimal digits
precision = 17;
format_code = "g";
break;
default:
throw new Error("Bad internall call");
}
// no need for buffer size calculation like in cpython
// Handle nan and inf
if(isNaN(val)) {
buf = "nan";
t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_NAN;
} else if (val === Infinity) {
buf = "inf";
t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_INFINITE;
} else if (val === -Infinity) {
buf = "-inf";
t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_INFINITE;
} else {
t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_FINITE;
if(flags & Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ADD_DOT_0) {
format_code = "g"; // "Z"; _PyOS_ascii_formatd converts "Z" to "g"
}
// ToDo: call snprintf here
// ToDo: call ascii_formatd
var format_str = "%";
format_str += flags & Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ALT ? "#" : "";
if(precision != null) {
format_str += ".";
format_str += precision;
}
format_str += format_code;
format_str = new Sk.builtin.str(format_str);
/**
* We cann call nb$remainder with val, because it gets unwrapped and it doesn't matter if it is
* already a javascript number. If we do not pass a float, we can't distinguish between ints and floats
* and therefore we can't adjust the sign of the zero accordingly
*/
buf = format_str.nb$remainder(new Sk.builtin.float_(val));
buf = buf.v; // get javascript string
}
/**
* Add sign when requested. It's convenient (esp. when formatting complex numbers) to
* include sign even for inf and nan.
*/
if(flags & Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN && buf[0] !== "-") {
buf = "+" + buf;
}
if(upper) {
// Convert to upper case
buf = buf.toUpperCase();
}
return buf;
};
/* PyOS_double_to_string's "flags" parameter can be set to 0 or more of: */
Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN = 0x01; // always add the sign
Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ADD_DOT_0 = 0x02; // if the result is an integer add ".0"
Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ALT = 0x04; // "alternate" formatting. it's format_code specific
/* PyOS_double_to_string's "type", if non-NULL, will be set to one of: */
Sk.builtin.complex.PyOS_double_to_string.Py_DTST_FINITE = 0;
Sk.builtin.complex.PyOS_double_to_string.Py_DTST_INFINITE = 1;
Sk.builtin.complex.PyOS_double_to_string.Py_DTST_NAN = 2;