Category talk:Wren-fmt: Difference between revisions
→Source code: Added 'jslwrite' and family methods.
(→Source code: Added support for signing floating point numbers.) |
(→Source code: Added 'jslwrite' and family methods.) |
||
(27 intermediate revisions by the same user not shown) | |||
Line 1:
===Source code===
<
/* Conv contains routines which do conversions between types. */
Line 107:
return "%(n)%(suffix)"
}
// Converts an integer to its unicode superscript equivalent.
static superscript(n) {
if (!(n is Num && n.isInteger)) Fiber.abort("Argument must be an integer.")
var ss = {
"0": "⁰", "1": "¹", "2": "²", "3": "³", "4": "⁴", "5": "⁵", "6": "⁶",
"7": "⁷", "8": "⁸", "9": "⁹", "-": "⁻", "+": "⁺", "e": "ᵉ"
}
return Fmt.i(0, n).map { |d| ss.containsKey(d) ? ss[d] : d }.join()
}
// Converts an integer to its unicode subscript equivalent.
static subscript(n) {
if (!(n is Num && n.isInteger)) Fiber.abort("Argument must be an integer.")
var ss = {
"0": "₀", "1": "₁", "2": "₂", "3": "₃", "4": "₄", "5": "₅", "6": "₆",
"7": "₇", "8": "₈", "9": "₉", "-": "₋", "+": "₊", "e": "ₑ"
}
return Fmt.i(0, n).map { |d| ss.containsKey(d) ? ss[d] : d }.join()
}
// Converts a numerator and denominator to their unicode fraction equivalent.
// If there is none, returns a string of the form "n/d".
static fraction(n, d) {
if (!(n is Num && n.isInteger && n > 0)) Fiber.abort("n must be a positive integer.")
if (!(d is Num && d.isInteger && d > 0)) Fiber.abort("d must be a positive integer.")
var fracs = {
"1/4": "¼", "1/2": "½", "3/4": "¾", "1/7": "⅐", "1/9": "⅑", "1/10": "⅒",
"1/3": "⅓", "2/3": "⅔", "1/5": "⅕", "2/5": "⅖", "3/5": "⅗", "4/5": "⅘",
"1/6": "⅙", "5/6": "⅚", "1/8": "⅛", "3/8": "⅜", "5/8": "⅝", "7/8": "⅞"
}
var frac = "%(n)/%(d)"
return fracs.containsKey(frac) ? fracs[frac] : frac
}
// Converts 's' to the plural form 'p' if and only if the absolute value of 'n' is not equal to 1.
static plural(n, s, p) { (n.abs != 1) ? p : s }
// Convenience version of the above method which forms the plural by adding 's' to the singular.
static plural(n, s) { (n.abs != 1) ? s + "s" : s }
// Makes a UTF-8 or byte string printable by replacing non-printable characters
// with escaped ones. If 'asBytes' is true, not just control characters
// but all bytes > 127 are regarded as non-printable.
static printable(s, asBytes) {
var res = ""
var chars = asBytes ? s.bytes : s.codePoints
var lets = "abtnvfr"
for (c in chars) {
if (c == -1) {
res = res + "\ufffd"
} else if (c == 0) {
res = res + "\\0"
} else if (c >= 7 && c <= 13) {
res = res + "\\" + lets[c - 7]
} else if (c == 27) {
res = res + "\\e"
} else if (c < 32 || c == 127 || (asBytes && c > 127)) {
res = res + "\\x" + Fmt.swrite("$02x", c)
} else if (!asBytes && c >= 128 && c < 160) {
res = res + esc + "\\u00" + Fmt.swrite("$02x", c)
} else {
res = res + String.fromCodePoint(c)
}
}
return res
}
// Returns the unicode infinity symbol.
static infinity { "∞" }
}
Line 299 ⟶ 369:
// Applies the 's' format to the kind (i.e. type) of 'v'.
static k(w, v) { s(w, v.type) }
// Converts a list of Unicode code points to a string and then applies the 's' format to it.
static u(w, v) {
if (!(v is List)) Fiber.abort("Second argument must be list of code points.")
var str = ""
for (c in v) str = str + String.fromCodePoint(c)
return s(w, str)
}
// Embeds a string or value 'v' in 'cc', a string with no more than two characters.
Line 315 ⟶ 393:
// Convenience version of the above which uses double quotes as the embedding characters.
static q(v) { "\"%(v)\"" }
// Maps the boolean value 'b' to "yes" if true or "no" if false
// and then applies the 's' format to the result.
static y(w, b) { Fmt.s(w, b ? "yes" : "no") }
// Formats a number 'n' (using 'h' format) to a maximum precision of 14 decimal places.
Line 349 ⟶ 431:
// Works like 'e' except that the exponent symbol 'e' is replaced by upper case 'E'.
static E(w, n, p) { Fmt.e(w, n, p).replace("e", "E") }
// Applies the 's' format to the name of a number 'n'.
static N(w, n) { Fmt.s(w, Name.fromNum(n)) }
// Applies the 's' format to the ordinal name of an integer 'i'.
static O(w, i) { Fmt.s(w, Name.ordinal(i)) }
// Applies the 's' format to the superior (or superscript) version of a number 'n'.
static S(w, n) { Fmt.s(w, Conv.superscript(n)) }
// Applies the 's' format to the the inferior (or subscript) version of a number 'n'.
static I(w, n) { Fmt.s(w, Conv.subscript(n)) }
// Applies the 's' format to a fraction f[0] / f[1 where 'f' is a two element list.
static F(w, f) { Fmt.s(w, Conv.fraction(f[0], f[1])) }
// Applies the 's' format to the plural of l[1] depending on l[0] and, if present, l[2] where 'l' is a
// two or three element list. Forms the plural by just adding "s" to l[1] if 'l' is a two element list.
static P(w, l) { Fmt.s(w, Conv.plural(l[0], l[1], l.count == 2 ? l[1] + "s" : l[2])) }
// Makes a byte string printable and applies the 's' format to the result.
static B(w, v) { Fmt.s(w, Conv.printable(v, true)) }
// Makes a UTF-8 string printable and applies the 's' format to the result.
static U(w, v) { Fmt.s(w, Conv.printable(v, false)) }
// Repeats 'v', a string or value, 'n' times.
static R(n, v) { (v is String) ? v * n : v.toString * n }
// Pads a number 'n' with leading spaces to a minimum width 'w' and a precision of 'p' decimal places.
Line 387 ⟶ 497:
static g(w, n, p) {
var f = f(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 400 ⟶ 511:
static h(w, n, p) {
var f = f(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 408 ⟶ 520:
return f
}
// Works like 'h' except, if right justified, removes any trailing spaces before rejustifying.
static j(w, n, p) { (w >= 0) ? rjust(w, h(w, n, p).trimEnd()) : h(w, n, p) }
// Works like 'j' except derives the number to be formatted from its name.
static l(w, name, p) { j(w, Name.toNum(name), p) }
// As above but pads with leading zeros instead of spaces.
Line 417 ⟶ 535:
// As above but prepends non-negative numbers with a '+' sign.
static fp(w, n, p) { signFloat_("f", w, n, p, "+") }
static gp(w, n, p) { signFloat_("g", w, n, p, "+") }
static hp(w, n, p) { signFloat_("h", w, n, p, "+") }
// As above but prepends non-negative numbers with a space.
static fm(w, n, p) { signFloat_("f", w, n, p, " ") }
static gm(w, n, p) { signFloat_("g", w, n, p, " ") }
static hm(w, n, p) { signFloat_("h", w, n, p, " ") }
// Private helper method for signing floating point numbers.
static signFloat_(fn, w, n, p, ch) {
var fmt = "$%(w).%(p)%(fn)"
if (n < 0) return swrite(fmt, n)
if (n > 0) return swrite(fmt, -n).replace("-",
return swrite(fmt, -1).replace("-1", "
}
Line 446 ⟶ 569:
static gc(w, n, p) {
var f = fc(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 459 ⟶ 583:
static hc(w, n, p) {
var f = fc(w, n, p)
if (f.contains("e")) return f
if (f.contains(".") && (f[-1] == "0" || f[-1] == " ")) {
var l1 = f.count
Line 468 ⟶ 593:
}
//
static jc(w, n, p) { (w >= 0) ? rjust(w, hc(w, n, p).trimEnd()) : hc(w, n, p) }
// Works like 'jc' except derives the number to be formatted from its name.
static lc(w, name, p) { jc(w, Name.toNum(name), p) }
// Applies the 'f' format to each component, x and y, of a complex number 'n'
// before joining them together in the form x ± yi.
static z(w, n, p) {
if (n is Num) return f(w, n, p)
Line 476 ⟶ 607:
var sign = (n.imag >= 0) ? " + " : " - "
var imag = f(w, n.imag.abs, p)
if (w < 0) imag = imag.trimEnd(" ")
return real + sign + imag + "i"
}
// Applies the 'fp' and 'f' formats respectively to each component, x and y, of a
// complex number 'n' before joining them together in the form x ± yi.
static zp(w, n, p) {
if (n is Num) return fp(w, n, p)
if (n.type.toString != "Complex") Fiber.abort("Argument must be a complex or real number.")
var real = fp(w, n.real, p)
var sign = (n.imag >= 0) ? " + " : " - "
var imag = f(w, n.imag.abs, p)
if (w < 0) imag = imag.trimEnd(" ")
return real + sign + imag + "i"
}
// Applies the 'fm' and 'f' formats respectively to each component, x and y, of a
// complex number 'n' before joining them together in the form x ± yi.
static zm(w, n, p) {
if (n is Num) return fm(w, n, p)
if (n.type.toString != "Complex") Fiber.abort("Argument must be a complex or real number.")
var real = fm(w, n.real, p)
var sign = (n.imag >= 0) ? " + " : " - "
var imag = f(w, n.imag.abs, p)
if (w < 0) imag = imag.trimEnd(" ")
return real + sign + imag + "i"
}
Line 485 ⟶ 641:
static g(w, n) { g(w, n, precision) }
static h(w, n) { h(w, n, precision) }
static j(w, n) { j(w, n, precision) }
static l(w, n) { l(w, n, precision) }
static z(w, n) { z(w, n, precision) }
static fz(w, n) { fz(w, n, precision) }
Line 492 ⟶ 650:
static gp(w, n) { gp(w, n, precision) }
static hp(w, n) { hp(w, n, precision) }
static zp(w, n) { zp(w, n, precision) }
static fm(w, n) { fm(w, n, precision) }
static gm(w, n) { gm(w, n, precision) }
static hm(w, n) { hm(w, n, precision) }
static zm(w, n) { zm(w, n, precision) }
static fc(w, n) { fc(w, n, precision) }
static gc(w, n) { gc(w, n, precision) }
static hc(w, n) { hc(w, n, precision) }
static jc(w, n) { jc(w, n, precision) }
static lc(w, n) { lc(w, n, precision) }
// Private worker method which calls a 'short name' method and returns its result.
static callFn_(fn, w, v, p) {
Line 512 ⟶ 677:
(fn == "n") ? n(w, v) :
(fn == "k") ? k(w, v) :
(fn == "u") ? u(w, v) :
(fn == "q") ? q(v) :
(fn == "y") ? y(w, v) :
(fn == "e") ? e(w, v, p) :
(fn == "E") ? Fmt.E(w, v, p) :
(fn == "N") ? Fmt.N(w, v) :
(fn == "O") ? Fmt.O(w, v) :
(fn == "S") ? Fmt.S(w, v) :
(fn == "I") ? Fmt.I(w, v) :
(fn == "F") ? Fmt.F(w, v) :
(fn == "P") ? Fmt.P(w, v) :
(fn == "B") ? Fmt.B(w, v) :
(fn == "U") ? Fmt.U(w, v) :
(fn == "R") ? Fmt.R(w, v) :
(fn == "f") ? f(w, v, p) :
(fn == "g") ? g(w, v, p) :
(fn == "h") ? h(w, v, p) :
(fn == "j") ? j(w, v, p) :
(fn == "l") ? l(w, v, p) :
(fn == "z") ? z(w, v, p) :
(fn == "dz") ? dz(w, v) :
Line 533 ⟶ 711:
(fn == "gp") ? gp(w, v, p) :
(fn == "hp") ? hp(w, v, p) :
(fn == "zp") ? zp(w, v, p) :
(fn == "fm") ? fm(w, v, p) :
(fn == "gm") ? gm(w, v, p) :
(fn == "hm") ? hm(w, v, p) :
(fn == "zm") ? zm(w, v, p) :
(fn == "dp") ? dp(w, v) :
(fn == "dm") ? dm(w, v) :
Line 541 ⟶ 724:
(fn == "fc") ? fc(w, v, p) :
(fn == "gc") ? gc(w, v, p) :
(fn == "hc") ? hc(w, v, p) :
(fn == "jc") ? jc(w, v, p) :
(fn == "lc") ? lc(w, v, p) : Fiber.abort("Method not recognized.")
}
Line 547 ⟶ 732:
// The method to be applied is specified (as a string) in 'fn'.
// The parameters to be passed to the method are specified in 'w' and 'p'
// 'p' is needed for 'e', 'E', 'f', 'g', 'h', 'j', 'l', 'z', 'fz', 'gz', 'hz', 'fp', 'gp'
// 'hp', 'zp', 'fm', 'gm', 'hm', 'zm', 'fc', 'gc', 'hc', 'jc' or '
// The resulting strings are then joined together using the separator 'sep'.
// having first applied the 'q' method, with parameter 'cc', to each of them.
Line 569 ⟶ 754:
static v(fn, w, seq, p) { v(fn, w, seq, p, ", ", "[]", "") }
static v(fn, w, seq) { v(fn, w, seq, precision, ", ", "[]", "") }
// As the 'v' method but abridges 'seq' to a maximum number
// of elements 'n' (non-overlapping) at either end or, if 'n' is negative,
// from the front only, using 'aa' as the separator. Doesn't abridge 'seq'
// unless at least one element would need to be suppressed.
static va(fn, w, seq, p, sep, bb, cc, n, aa) {
if (!(n is Num && n.isInteger && n.abs >= 1)) Fiber.abort("'n' must be a non-zero integer.")
if (!(aa is String)) Fiber.abort("'aa' must be a string.")
var c = seq.count
if (c <= ((n < 0) ? -n : 2*n)) return Fmt.v(fn, w, seq, p, sep, bb, cc)
var left = (seq is List) ? seq[0...n.abs] : seq.take(n.abs)
var sleft = Fmt.v(fn, w, left, p, sep, "", cc) + sep
var sright = ""
if (n > 0) {
var right = (seq is List) ? seq[-n..-1] : seq.skip(c - n)
sright = sep + Fmt.v(fn, w, right, p, sep, "", cc)
}
var res = sleft + aa + sright
if (bb != "") res = Fmt.q(res, bb)
return res
}
// Applies a 'short' formatting method to each element of a two-dimensional
Line 603 ⟶ 809:
// $[flag][width][.precision][letter] of which all bracketed items except [letter] are optional.
// The letter must be one of the 'short' methods:
// a, b, B, c, d, e, E, f, F, g, h, i, I, j, k, l, m, n, N, o, O, P, q, r, R, s, S, t, u, U, x, X, y or z.
// If present, the flag (there can only be one) must be one of the following:
// + always prints a + or - sign ('dp', 'fp', 'gp', 'hp' or '
// (space) leaves a space for the sign but only prints minus ('dm',
// , commatizes the following number ('dc', 'rc', 'sc', 'ic', 'fc', 'gc, 'hc', 'jc' or '
// # adds the appropriate prefix for the number formats: b, t, o, d, x and X
// * reads the width from the argument before the one to be formatted
Line 615 ⟶ 821:
// It doesn't include any '#' flag prefix. If [width] is absent, a width of one is passed.
// If present, the precision is the number of decimal places to be passed to the appropriate
// 'e', 'E', 'f', 'g', 'h', 'j', 'l' or 'z' style method. If absent, the default precision is passed.
// Where any optional item is inappropriate to the method being used it is simply ignored.
// Where one of the arguments is a sequence (other than a string) this method senses it
// and applies the 'v' method to it. However, the 'sep' parameter is always a single space
// and the 'bb' and 'cc' parameters are always empty strings. Finally, to print a literal
// dollar symbol use $$.
static slwrite(fmt, a) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
Line 672 ⟶ 879:
var fn = ""
var ds = ""
if ("
fn = Conv.itoc(cp)
} else if (cp == 42) { // star
Line 707 ⟶ 914:
if (fn == "") {
if (!"
Fiber.abort("Unrecognized character in format string.")
}
Line 720 ⟶ 927:
fn = "dc"
}
} else if ((fn == "f" || fn == "g" || fn == "h" || fn == "z") && plus) {
fn = fn + "p"
} else if ((fn == "f" || fn == "g" || fn == "h" || fn == "z") && space) {
fn = fn + "m"
} else if ((fn == "r" || fn == "s" || fn == "i" || fn == "f" ||
fn == "g" || fn == "h" || fn == "j" || fn == "l") && comma) {
fn = fn + "c"
}
Line 734 ⟶ 943:
if (next < a.count) {
var e = a[next]
if ((e is Sequence) && !(e is String) &&
if (hash && "btodxX".contains(fn[0])) {
var rr = []
Line 771 ⟶ 980:
}
// Convenience versions of the 'slwrite' method which allow up to
// to be passed individually rather than in a list.
static swrite(fmt, a1, a2, a3, a4, a5
static swrite(fmt, a1, a2, a3, a4, a5)
static swrite(fmt, a1, a2, a3, a4)
static swrite(fmt, a1, a2, a3)
static swrite(fmt, a1, a2)
static swrite(fmt, a1) { slwrite(fmt, [a1]) }
// Applies slwrite to the arguments and then 'writes' it (no following \n) to stdout.
static write(fmt, a1, a2, a3, a4, a5, a6) { System.write(slwrite(fmt, [a1, a2, a3, a4, a5, a6])) }
static write(fmt, a1, a2, a3, a4, a5) { System.write(slwrite(fmt, [a1, a2, a3, a4, a5])) }
static write(fmt, a1, a2, a3, a4) { System.write(slwrite(fmt, [a1, a2, a3, a4])) }
static write(fmt, a1, a2, a3) { System.write(slwrite(fmt, [a1, a2, a3])) }
static write(fmt, a1, a2) { System.write(slwrite(fmt, [a1, a2])) }
static
static lwrite(fmt, a) { System.write(slwrite(fmt, a)) }
// Applies slwrite to the arguments and then 'prints' it (with a following \n) to stdout.
static print(fmt, a1, a2, a3, a4, a5, a6) { System.print(slwrite(fmt, [a1, a2, a3, a4, a5, a6])) }
static print(fmt, a1, a2, a3, a4, a5) { System.print(slwrite(fmt, [a1, a2, a3, a4, a5])) }
static print(fmt, a1, a2, a3, a4) { System.print(slwrite(fmt, [a1, a2, a3, a4])) }
static print(fmt, a1, a2, a3) { System.print(slwrite(fmt, [a1, a2, a3])) }
static print(fmt, a1, a2) { System.print(slwrite(fmt, [a1, a2])) }
static
static lprint(fmt, a) { System.print(slwrite(fmt, a)) }
// Synomyms of corresponding methods in System class - useful for aligning code.
static write(object) { System.write(object) }
static writeAll(sequence) { System.writeAll(sequence) }
static print() { System.print() }
static print(object) { System.print(object) }
static printAll(sequence) { System.printAll(sequence) }
// Gets or sets the separator for the 'jslwrite' method. The default is a single space.
static separator { (__separator != null) ? __separator : " " }
static separator=(s) { __separator = (s is String) ? s : s.toString }
// Returns a string formed from joining together the string representation of
// the elements of the list or sequence 'a' using the current separator.
static jslwrite(a) { a.join(separator) }
// Convenience versions of the 'jslwrite' method which allow from 2 to 6 arguments
// to be passed individually rather than in a list or sequence
static jswrite(a1, a2, a3, a4, a5, a6) { jsl.write([a1, a2, a3, a4, a5, a6]) }
static jswrite(a1, a2, a3, a4, a5) { jsl.write([a1, a2, a3, a4, a5]) }
static jswrite(a1, a2, a3, a4) { jsl.write([a1, a2, a3, a4]) }
static jswrite(a1, a2, a3) { jsl.write([a1, a2, a3]) }
static jswrite(a1, a2) { jsl.write([a1, a2]) }
// Applies jslwrite to the arguments and then 'writes' it (no following \n) to stdout.
static jwrite(a1, a2, a3, a4, a5, a6) { System.write(jslwrite([a1, a2, a3, a4, a5, a6])) }
static jwrite(a1, a2, a3, a4, a5) { System.write(jslwrite([a1, a2, a3, a4, a5])) }
static jwrite(a1, a2, a3, a4) { System.write(jslwrite([a1, a2, a3, a4])) }
static jwrite(a1, a2, a3) { System.write(jslwrite([a1, a2, a3])) }
static jwrite(a1, a2) { System.write(jslwrite([a1, a2])) }
static jlwrite(a) { System.write(jslwrite(a)) }
// Applies jslwrite to the arguments and then 'prints' it (with a following \n) to stdout.
static jprint(a1, a2, a3, a4, a5, a6) { System.print(jslwrite([a1, a2, a3, a4, a5, a6])) }
static jprint(a1, a2, a3, a4, a5) { System.print(jslwrite([a1, a2, a3, a4, a5])) }
static jprint(a1, a2, a3, a4) { System.print(jslwrite([a1, a2, a3, a4])) }
static jprint(a1, a2, a3) { System.print(jslwrite([a1, a2, a3])) }
static jprint(a1, a2) { System.print(jslwrite([a1, a2])) }
static jlprint(a) { System.print(jslwrite(a)) }
// Prints (with a following \n) a sequence 'a' to stdout in tabular form
// with a maximum of 'rowSize' elements per row. 'fmt' is applied individually
// to each element and formatted elements are separated by 'sep'.
static tprint(fmt, a, rowSize, sep) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
if (!(a is Sequence)) Fiber.abort("Second argument must be a sequence.")
if (!((rowSize is Num) && rowSize.isInteger && rowSize > 0)) {
Fiber.abort("Third argument must be a positive integer.")
}
var ac = a.count
var count = 0
for (e in a) {
Fmt.write(fmt, e)
count = count + 1
if (count % rowSize == 0 || count == ac) {
System.print()
} else System.write(sep)
}
}
// Prints (with a following \n) a sequence 'a' to stdout in columnar form
// with a maximum of 'colSize' elements per column. 'fmt' is applied individually
// to each element and formatted elements are separated by 'sep'.
static cprint(fmt, a, colSize, sep) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
if (!(a is Sequence)) Fiber.abort("Second argument must be a sequence.")
if (!((colSize is Num) && colSize.isInteger && colSize > 0)) {
Fiber.abort("Third argument must be a positive integer.")
}
if (!(a is List)) a = a.toList
var ac = a.count
for (i in 0...colSize) {
var j = i
while (true) {
Fmt.write(fmt, a[j])
j = j + colSize
if (j >= ac) break
System.write(sep)
}
System.print()
}
}
// Convenience versions of the above methods which use a single space for the separator.
static tprint(fmt, a, rowSize) { tprint(fmt, a, rowSize, " ") }
static cprint(fmt, a, colSize) { cprint(fmt, a, colSize, " ") }
// Prints (with a following \n) an array 'a' to stdout using a typical layout.
Line 800 ⟶ 1,098:
// The settings for the other parameters are:
// 'fn' = "f" for numbers, "z" for complex numbers,"s" otherwise
// ('p' is ignored for latter) 'sep' = " ", 'cc' = "".
// The 'rns' parameter, if true, leaves a space for the sign of a real number but only prints minus.
static aprint(a, w, p, bb, rns) {
if (!(a is Sequence)) Fiber.abort("Second argument must be a sequence.")
if (!(a is List)) a = a.toList
var fn = (a.count > 0 && (a[0] is Num)) ? "f" :
(a.count > 0 && (a[0].type.toString == "Complex")) ? "z" : "s"
if (rns && fn != "s") fn = fn + "m"
System.print(Fmt.v(fn, w, a, p, " ", bb, ""))
}
Line 809 ⟶ 1,111:
// Convenience versions of the above method which use default values for
// some parameters.
static aprint(a, w, p, bb) { aprint(a, w, p,
static aprint(a, w, p) { aprint(a, w,
static aprint(a, w) { aprint(a,
static aprint(a) { aprint(a, 0, precision, "[]", false) }
// Prints (with a following \n) a matrix 'm' to stdout using a typical layout.
Line 820 ⟶ 1,123:
// 'fn' = "f" for numbers, "z" for complex numbers, "s" otherwise
// ('p' is ignored for latter) 'sep' = " ", 'cc' = "", 'ss' = "\n".
// The rns' parameter, if true, leaves a space for the sign of a real number but only prints minus.
static mprint(m, w, p, bb, rns) {
if (
var fn = (m.count > 0 && m[0].count > 0 && (m[0][0] is Num)) ? "f" :
(m.count > 0 && m[0].count > 0 && (m[0][0].type.toString == "Complex")) ? "z" : "s"
if (rns && fn != "s") fn = fn + "m"
System.print(Fmt.v2(fn, w, m, p, " ", bb, "", "\n"))
}
// Convenience versions of the above method which use default values for
// some parameters.
static mprint(m, w, p, bb) { mprint(m, w, p,
static mprint(m, w, p) { mprint(m, w,
static mprint(m, w) { mprint(m,
static mprint(m) { mprint(m, 0, precision, "|", false) }
// Formats a polynomial as a string.
// Polynomials are represented by an ordered list of coefficients
// from the term with the highest degree down to the constant term.
// Unless there is only one term, terms with zero coefficents are suppressed.
// Unless it's the constant term, the '1' in coefficients of exactly ± 1 is also suppressed.
// 'fmt' is applied to each coefficient, 'symbol' is the exponentiation
// symbol (e.g. "^") and 'variable' (e.g. "x") is the variable name.
// If symbol = "", unicode superscript characters are used for the degree.
static spwrite(fmt, coefs, symbol, variable) {
if (!(fmt is String)) Fiber.abort("First argument must be a string.")
if (!(coefs is List) || coefs.count == 0 || !(coefs[0] is Num)) {
Fiber.abort("Second argument must be a non-empty ordered list of numbers.")
}
if (!(symbol is String)) Fiber.abort("Third argument must be a string.")
if (!(variable is String)) Fiber.abort("Fourth argument must be a string.")
var degree = coefs.count - 1
if (degree == 0 || coefs.all { |c| c == 0 }) return Fmt.swrite(fmt, coefs[0])
var p = ""
for (i in 0..degree) {
var coef = coefs[i]
var pow = degree - i
if (coef == 0) continue
if (coef > 0) {
var t = Fmt.swrite(fmt, coef)
if (pow > 0 && coef == 1 && t[-1] == "1") t = t[0...-1]
p = p + " + " + t
} else {
var t = Fmt.swrite(fmt, -coef)
if (pow > 0 && coef == -1 && t[-1] == "1") t = t[0...-1]
p = p + " - " + t
}
if (pow > 1) {
if (symbol != "") {
p = p + Fmt.swrite("$s$s$d", variable, symbol, pow)
} else {
p = p + Fmt.swrite("$s$S", variable, pow)
}
} else if (pow == 1) {
p = p + variable
}
}
p = p.startsWith(" + ") ? p[3..-1] : "-" + p[3..-1]
return p
}
// Prints a polynomial without (pwrite) or with (pprint) a following \n to stdout.
static pwrite(fmt, coefs, symbol, variable) {
System.write(spwrite(fmt, coefs, symbol, variable))
}
static pprint(fmt, coefs, symbol, variable) {
System.print(spwrite(fmt, coefs, symbol, variable))
}
}
/* Name contains infrastructure and routines for converting numbers to their English names. */
class Name {
// Private method to initialize static fields.
static init_() {
__uk = false
__neg = "minus"
__point = "point"
__zero = "zero"
__small = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven",
"twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
]
__tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
__illions = [
"", " thousand", " million", " billion"," trillion", " quadrillion", " quintillion",
" sextillion", " septillion", " octillion", " nonillion", " decillion"
]
__irregularOrdinals = {
"nought": "noughth",
"one": "first",
"two": "second",
"three": "third",
"five": "fifth",
"eight": "eighth",
"nine": "ninth",
"twelve": "twelfth"
}
__revIrregulars = {}
for (me in __irregularOrdinals) __revIrregulars[me.value] = me.key
__names = {}
for (i in 0..19) __names[__small[i]] = i
for (i in 2..9) __names[__tens[i]] = i * 10
__names["hundred"] = 100
for (i in 1..11) __names[__illions[i][1..-1]] = 10.pow(i * 3)
__names["nought"] = 0
__zeros = ["zero", "nought", "nil", "none", "nothing"]
__points = ["point", "dot", "spot"]
}
// Private helper function. Converts ASCII string to lower case.
static lower_(s) { s.bytes.map { |b|
return String.fromByte((b >= 65 && b <= 90) ? b + 32 : b)
}.join() }
// Gets or sets whether names are to expressed in UK English i.e. 'and' is used to connect
// double or single digit numbers with larger numbers. The default is 'false'.
static uk { __uk }
static uk=(b) { __uk = (b is Bool) ? b : __uk}
// Gets or sets the prefix word for negative numbers. The default is "minus" though another
// possibility is "negative".
static negative { __neg }
static negative=(n) { __neg = (n is String) ? n : __neg }
// Gets or sets the word for the decimal point in non-integral numbers. The default is "point"
// though other possibilities are "dot" or "spot".
static point { __point }
static point=(p) { __point = (p is String) ? p : __point }
// Gets or sets the word for the number '0'. The default is "zero" though another
// possibility is 'nought'.
static zero { __zero }
static zero=(z) { __zero = (z is String) ? __small[0] = z : __zero }
// Returns the name of a number which can be positive or negative and can include a decimal point.
// Note that this is unsafe for numbers with an absolute value >= 2^53 but may work in some cases.
static fromNum(n) {
if (!(n is Num)) Fiber.abort("'n' must be a number.")
if (n.isInfinity || n.isNan) return Fmt.s(0, n)
var f = 0
if (!n.isInteger) {
f = n.fraction
n = n.truncate
}
var and = __uk ? "and " : ""
var t = ""
if (n < 0) {
t = __neg + " "
n = -n
}
if (n < 20) {
t = t + __small[n]
} else if (n < 100) {
t = t + __tens[(n/10).floor]
var s = n % 10
if (s > 0) t = t + "-" + __small[s]
} else if (n < 1000) {
t = t + __small[(n/100).floor] + " hundred"
var s = n % 100
if (s > 0) t = t + " " + and + fromNum(s)
} else {
var sx = ""
var i = 0
while (n > 0) {
var p = n % 1000
n = (n/1000).floor
if (p > 0) {
var ix = fromNum(p) + __illions[i]
if (sx != "") ix = ix + " " + sx
sx = ix
}
i = i + 1
}
t = t + sx
}
if (f > 0) {
t = t + " " + __point
for (d in f.toString.skip(2)) {
t = t + " %(__small[Num.fromString(d)])"
}
}
return t
}
// Returns the ordinal name of an integer (including negative integers).
// Note that this is unsafe for integers with an absolute value >= 2^53 but may work in some cases.
static ordinal(n) {
if (!(n is Num && n.isInteger)) Fiber.abort("'n' must be an integer.")
var s = fromNum(n)
var r = s[-1..0]
var i1 = r.indexOf(" ")
if (i1 != -1) i1 = s.count - 1 - i1
var i2 = r.indexOf("-")
if (i2 != -1) i2 = s.count - 1 - i2
var i = (i1 > i2) ? i1 : i2
i = i + 1
var x = __irregularOrdinals[s[i..-1]]
if (x) {
return s[0...i] + x
} else if (s[-1] == "y") {
return s[0...i] + s[i..-2] + "ieth"
} else {
return s[0...i] + s[i..-1] + "th"
}
}
// Translates the name of a number or ordinal integer into that number and returns it.
// Ignores case and custom settings but recognizes all suggested alternatives and some others.
static toNum(name) {
if (!(name is String) || name == "") Fiber.abort("'name' must be a non-empty string.")
name = lower_(name).replace(",", " ").replace("-", " ").replace(" and", "")
var words = name.split(" ").where { |w| w != "" }.toList
var isNegative = words[0] == "minus" || words[0] == "negative"
if (isNegative || words[0] == "plus") words = words[1..-1]
if (words[0] == "a") words[0] = "one"
if (words[-1].endsWith("ieth")) {
words[-1] = words[-1][0..-5] + "y"
} else if(__revIrregulars.containsKey(words[-1])) {
words[-1] = __revIrregulars[words[-1]]
} else if (words[-1].endsWith("th")) {
words[-1] = words[-1][0..-3]
}
var size = words.count
if (size == 1 && __zeros.contains(words[0])) return 0
if (size == 1 && words[0] == "nan") return Num.nan
if (size == 1 && words[0] == "infinity") return isNegative ? -Num.infinity : Num.infinity
var ix = -1
for (p in __points) {
if ((ix = words.indexOf(p)) >= 0) break
}
if (ix == 0) {
words.insert(0, "zero")
ix = 1
}
var dec = ""
if (ix > 0) {
for (word in words[ix+1..-1]) dec = dec + __names[word].toString
size = ix
}
var multiplier = 1
var lastNum = -1
var sum = 0
for (i in size-1..0) {
var num = __names[words[i]]
if (!num) Fiber.abort("'%(words[i])' is not a valid cardinal number.")
if (num == lastNum) Fiber.abort("'%(name)' is not a well formed numeric string.")
if (num >= 1000) {
if (lastNum >= 100) {
Fiber.abort("'%(name)' is not a well formed numeric string.")
}
multiplier = num
if (i == 0) sum = sum + multiplier
} else if (num >= 100) {
multiplier = multiplier * 100
if (i == 0) sum = sum + multiplier
} else if (num >= 20) {
if (lastNum >= 10 && lastNum <= 90) {
Fiber.abort("'%(name)' is not a well formed numeric string.")
}
sum = sum + num*multiplier
} else {
if (lastNum >= 1 && lastNum <= 90) {
Fiber.abort("'%(name)' is not a well formed numeric string.")
}
sum = sum + num*multiplier
}
lastNum = num
}
if (dec != "") sum = sum + Num.fromString("0." + dec)
return (isNegative) ? -sum : sum
}
}
Name.init_()</syntaxhighlight>
|