Category talk:Wren-fmt: Difference between revisions
→Source code: Added 'jslwrite' and family methods.
(→Source code: Added a generic method for tabular printing.) |
(→Source code: Added 'jslwrite' and family methods.) |
||
(24 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 323 ⟶ 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 357 ⟶ 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 395 ⟶ 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 408 ⟶ 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 416 ⟶ 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 425 ⟶ 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 454 ⟶ 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 467 ⟶ 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 476 ⟶ 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 484 ⟶ 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 493 ⟶ 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 500 ⟶ 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 522 ⟶ 679:
(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 542 ⟶ 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 550 ⟶ 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 556 ⟶ 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 578 ⟶ 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 612 ⟶ 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 624 ⟶ 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
Line 682 ⟶ 879:
var fn = ""
var ds = ""
if ("
fn = Conv.itoc(cp)
} else if (cp == 42) { // star
Line 717 ⟶ 914:
if (fn == "") {
if (!"
Fiber.abort("Unrecognized character in format string.")
}
Line 730 ⟶ 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 744 ⟶ 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 781 ⟶ 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
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.")
Line 814 ⟶ 1,055:
Fiber.abort("Third argument must be a positive integer.")
}
var count = 0
for (e in a) {
Fmt.write(fmt, e)
count = count + 1
if (count % rowSize == 0
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 829 ⟶ 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 838 ⟶ 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 849 ⟶ 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>
|