灵符lingfu

LingSys

↓ 0 downloads

Your machine's memory, GPU, motherboard and network, drawn and played in the Ling design system: LingSys, a small build for USB liquid-cooler displays, LingNet for IPv4/IPv6, DNS and packets, and a LingOS edition. 64-bit and 32-bit Windows builds included.

Keywords system-monitor, visualization, network, ipv6, gpu, lcd, lingos, ling
lingfu install LingSys
LicenseMIT
AuthorsLing Lin <taellinglin@gmail.com>
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (48)

src/40_memory.ling
# ───────────────────────────── data ──────────────────────────────────────────
# mem.snap, see sampler.ps1. Returns [good, g, r, private_all, names, ws, priv, ior, iow]
fn parse_snap(text) {
    bind g = []
    bind r = []
    bind pall = 0.0
    bind names = []
    bind ws = []
    bind priv = []
    bind ior = []
    bind iow = []
    bind lines = split(text, "\n")
    bind i = 0
    while i < len(lines) {
        bind f = split(trim(lines[i]), "|")
        bind tag = f[0]
        if tag == "g" {
            bind g = []
            bind j = 1
            while j < len(f) {
                bind g = list_push(g, to_number(f[j]))
                bind j = j + 1
            }
        }
        if tag == "r" {
            bind r = []
            bind j = 1
            while j < len(f) {
                bind r = list_push(r, to_number(f[j]))
                bind j = j + 1
            }
        }
        if tag == "t" { bind ts = to_number(f[1]) }
        if tag == "s" { bind pall = to_number(f[1]) }
        if tag == "p" {
            if len(f) >= 8 {
                bind names = list_push(names, f[1])
                bind ws = list_push(ws, to_number(f[3]))
                bind priv = list_push(priv, to_number(f[4]))
                bind ior = list_push(ior, to_number(f[5]))
                bind iow = list_push(iow, to_number(f[6]))
            }
        }
        bind i = i + 1
    }
    bind good = 0
    if len(g) >= 11 {
        if len(r) >= 8 { bind good = 1 }
    }
    return [good, g, r, pall, names, ws, priv, ior, iow, ts]
}

# the seven category sizes in MB, from the sampler's counters
fn categories(g, pall) {
    bind total = g[0]
    bind avail = g[1]
    bind free = g[2]
    bind standby = g[3]
    bind modified = g[4]
    bind kernel = g[5] + g[6]
    bind comp = g[7]
    bind inuse = max(total - avail - modified, 0.0)
    bind data = clamp(pall - comp, 0.0, max(inuse - kernel - comp, 0.0))
    bind mapped = max(inuse - kernel - comp - data, 0.0)
    return [kernel, standby, data, modified, mapped, comp, free]
}

# a made-up machine for when there is no sampler: 16 GB, breathing, with bursts of paging
fn sim_cats(t) {
    bind total = 16384.0
    bind kernel = 900.0 + 150.0 * sin(t * 0.13)
    bind standby = 4200.0 + 1600.0 * sin(t * 0.05 + 1.0)
    bind data = 5200.0 + 2200.0 * sin(t * 0.031 + 2.0) + 600.0 * sin(t * 0.21)
    bind modified = 130.0 + 110.0 * (sin(t * 0.4) + 1.0)
    bind comp = 700.0 + 300.0 * sin(t * 0.07)
    bind mapped = 1500.0 + 500.0 * sin(t * 0.09 + 3.0)
    bind free = max(total - kernel - standby - data - modified - comp - mapped, 200.0)
    return [kernel, standby, data, modified, mapped, comp, free]
}

fn sim_rates(t) {
    bind b1 = pow(abs(sin(t * 0.37)), 6.0)
    bind b2 = pow(abs(sin(t * 0.23 + 1.0)), 8.0)
    bind b3 = pow(abs(sin(t * 0.51 + 2.0)), 5.0)
    return [3000.0 + 2500.0 * sin(t * 0.7) + 40000.0 * b3, 40.0 + 4000.0 * b1, 20.0 + 3000.0 * b2]
}


# weighted pick: w is a list of weights, u in [0,1)
fn pick(w, u) {
    bind sum = 0.0
    bind i = 0
    while i < len(w) {
        bind sum = sum + w[i]
        bind i = i + 1
    }
    bind v = u * sum
    bind i = 0
    while i < len(w) - 1 {
        if v < w[i] { return i }
        bind v = v - w[i]
        bind i = i + 1
    }
    return len(w) - 1
}

# Hilbert curve table for a 32 x 32 page map: flat [x0, y0, x1, y1, ...]
fn hilbert_table(n) {
    bind out = []
    bind d = 0
    while d < n * n {
        bind t = d
        bind x = 0
        bind y = 0
        bind s = 1
        while s < n {
            bind rx = floor(t / 2) % 2
            bind ry = (t % 2 + rx) % 2
            if ry == 0 {
                if rx == 1 {
                    bind x = s - 1 - x
                    bind y = s - 1 - y
                }
                bind tmp = x
                bind x = y
                bind y = tmp
            }
            bind x = x + s * rx
            bind y = y + s * ry
            bind t = floor(t / 4)
            bind s = s * 2
        }
        bind out = list_push(out, x)
        bind out = list_push(out, y)
        bind d = d + 1
    }
    return out
}

# ───────────────────────────── pulses ────────────────────────────────────────
# EV is flat, six numbers per pulse: kind (0 read circle, 1 write square), category,
# born, seed, magnitude, process index.

fn ev_add(ev, kind, cat, born, seed, mag, proc) {
    bind ev = list_push(ev, kind)
    bind ev = list_push(ev, cat)
    bind ev = list_push(ev, born)
    bind ev = list_push(ev, seed)
    bind ev = list_push(ev, mag)
    bind ev = list_push(ev, proc)
    return ev
}

fn ev_cull(ev, t, life) {
    bind out = []
    bind i = 0
    while i < len(ev) {
        if t - ev[i + 2] < life {
            bind j = 0
            while j < 6 {
                bind out = list_push(out, ev[i + j])
                bind j = j + 1
            }
        }
        bind i = i + 6
    }
    return out
}

# ───────────────────────────── view 1: RINGS ─────────────────────────────────
fn ring_geom(c, R) {
    bind r1 = R * (1.0 - c * 0.1)
    bind band = R * 0.082
    return [r1, r1 - band, band]
}

fn ring_spin(Z, c) {
    bind dir = 1.0
    if c % 2 == 1 { bind dir = 0.0 - 1.0 }
    return Z[Z_ROT] * dir * (0.35 + 0.12 * c) + Z[Z_T] * dir * 0.03 * (c + 1)
}

fn view_rings(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind cx = W / 2.0
    bind cy = H / 2.0
    bind R = min(W, H) * 0.5 - 16.0
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind cats = Z[Z_CATS]
    bind total = Z[Z_TOTAL]
    bind en = Z[Z_ENERGY]
    bind c = 0
    while c < 7 {
        bind gm = ring_geom(c, R)
        bind r1 = gm[0]
        bind r0 = gm[1]
        bind band = gm[2]
        bind frac = cats[c] / total
        bind spin = ring_spin(Z, c)
        bind e = en[c]
        # the two rails
        col(c, sat * 0.7, 26.0 + 14.0 * e, hs)
        draw_circle(cx, cy, r1)
        draw_circle(cx, cy, r0)
        # crosshatch over the occupied sector, a second angle once the ring is busy
        bind sweep = max(frac * TAU, 0.03)
        bind da = 0.0215
        bind n = floor(sweep / da)
        bind k = 0
        col(c, sat, 36.0 + 26.0 * e, hs)
        while k < n {
            bind a = spin + k * da
            pline(cx, cy, r0, a, r1, a + 0.05 + 0.05 * e)
            bind k = k + 1
        }
        if e > 0.35 {
            bind k = 0
            col(c, sat, 30.0 + 22.0 * e, hs)
            while k < n {
                bind a = spin + k * da
                pline(cx, cy, r0, a + 0.05, r1, a)
                bind k = k + 2
            }
        }
        col(c, sat, 62.0 + 28.0 * e, hs)
        pline(cx, cy, r0 - 4.0, spin, r1 + 4.0, spin)
        pline(cx, cy, r0 - 4.0, spin + sweep, r1 + 4.0, spin + sweep)
        # ring text: name, size, share
        bind label = CAT_NAME[c] + " " + fmt_mb(cats[c]) + " " + num_str(round(frac * 1000.0) / 10.0) + "% "
        ring_text(label, cx, cy, r0 + band * 0.06, r1 - band * 0.06, spin * 0.6 + 1.0, 74.0 + 20.0 * e, c, sat, hs)
        bind c = c + 1
    }
    # hub: used share in Daemon, ticks turning
    bind hr = R * 0.3 - 6.0
    ink(34.0)
    draw_circle(cx, cy, hr)
    bind k = 0
    while k < 60 {
        bind a = k * TAU / 60.0 + Z[Z_ROT] * 0.5
        bind tl = 5.0
        if k % 5 == 0 { bind tl = 11.0 }
        pline(cx, cy, hr - tl, a, hr, a)
        bind k = k + 1
    }
    bind used = num_str(round(Z[Z_USED] * 100.0)) + "%"
    col(2, sat, 78.0, hs)
    daemon_text(used, cx - text_w(used, hr * 0.5) / 2.0, cy - hr * 0.08, hr * 0.5)
    ink(55.0)
    daemon_text("USED", cx - text_w("USED", hr * 0.2) / 2.0, cy + hr * 0.4, hr * 0.2)
    # pulses ride their ring
    bind ev = Z[Z_EV]
    bind i = 0
    while i < len(ev) {
        bind age = t - ev[i + 2]
        bind p = age / 1.8
        bind cc = ev[i + 1]
        bind gm = ring_geom(cc, R)
        bind rm = (gm[0] + gm[1]) / 2.0
        bind a = ring_spin(Z, cc) + ev[i + 3] * TAU
        bind x = px(cx, rm, a)
        bind y = py(cy, rm, a)
        col(cc, sat, (1.0 - p) * (55.0 + 40.0 * ev[i + 4]), hs)
        if ev[i] == 0 {
            draw_circle(x, y, gm[2] * (0.25 + 1.5 * p))
            draw_circle(x, y, gm[2] * (0.1 + 0.9 * p))
        } else {
            square(x, y, gm[2] * (1.1 - 0.8 * p), a + p * 2.0)
            square(x, y, gm[2] * (0.6 - 0.4 * p), a - p * 2.0)
        }
        bind i = i + 6
    }
}

# ───────────────────────────── view 2: TILES ─────────────────────────────────
fn cell_cat(i, cum) {
    bind k = 0
    while k < 6 {
        if i < cum[k] { return ORDER[k] }
        bind k = k + 1
    }
    return ORDER[6]
}

fn view_tiles(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind cats = Z[Z_CATS]
    bind total = Z[Z_TOTAL]
    bind en = Z[Z_ENERGY]
    bind hil = Z[Z_HIL]
    bind S = min(H * 0.76, W * 0.5)
    bind cs = S / 32.0
    bind mx = W * 0.36
    bind my = H * 0.5
    bind tilt = 0.09 * sin(t * 0.07) + 0.07 * sin(Z[Z_ROT] * 0.8)
    bind ca = cos(tilt)
    bind sa = sin(tilt)
    # cumulative cell counts along the curve, in ORDER
    bind cum = []
    bind run = 0.0
    bind k = 0
    while k < 7 {
        bind run = run + cats[ORDER[k]] / total * 1024.0
        bind cum = list_push(cum, round(run))
        bind k = k + 1
    }
    bind i = 0
    while i < 1024 {
        bind c = cell_cat(i, cum)
        bind gx = hil[i * 2] - 15.5
        bind gy = hil[i * 2 + 1] - 15.5
        bind x = mx + (gx * ca - gy * sa) * cs
        bind y = my + (gx * sa + gy * ca) * cs
        bind hv = fract(sin(i * 12.9898) * 43758.5453)
        bind hv = fract(hv + 0.18 * sin(t * 0.6 + i * 0.37) + 1.0)
        bind e = en[c]
        bind h = cs * 0.46
        col(c, sat * 0.8, 20.0 + 16.0 * hv + 14.0 * e, hs + (hv - 0.5) * 10.0)
        square(x, y, h, tilt)
        if c != 6 {
            bind nl = 1 + floor(hv * 3.0 + e * 2.0)
            bind m = 0
            col(c, sat, 30.0 + 26.0 * hv + 22.0 * e, hs + (hv - 0.5) * 10.0)
            while m < nl {
                bind u = (m + 0.5) / nl
                bind s0 = (u * 2.0 - 1.0) * h
                bind an = tilt + ANG1[c]
                bind dx = cos(an) * h * 0.9
                bind dy = sin(an) * h * 0.9
                bind ox = (0.0 - sin(an)) * s0
                bind oy = cos(an) * s0
                draw_line(x + ox - dx, y + oy - dy, x + ox + dx, y + oy + dy)
                bind m = m + 1
            }
        } else {
            col(6, sat * 0.5, 18.0, hs)
            draw_pixel(x, y)
        }
        bind i = i + 1
    }
    ink(30.0)
    square(mx, my, S * 0.5 + cs * 0.9, tilt)
    # pulses: read = circles leaving a cell, write = squares closing on it
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind age = t - ev[j + 2]
        bind p = age / 1.8
        bind c = ev[j + 1]
        bind kk = 0
        while kk < 7 {
            if ORDER[kk] == c {
                bind lo = 0
                if kk > 0 { bind lo = cum[kk - 1] }
                bind span = max(cum[kk] - lo, 1.0)
                bind idx = min(floor(lo + ev[j + 3] * span), 1023)
                bind gx = hil[idx * 2] - 15.5
                bind gy = hil[idx * 2 + 1] - 15.5
                bind x = mx + (gx * ca - gy * sa) * cs
                bind y = my + (gx * sa + gy * ca) * cs
                col(c, sat, (1.0 - p) * (60.0 + 35.0 * ev[j + 4]), hs)
                if ev[j] == 0 {
                    draw_circle(x, y, cs * (0.5 + 3.5 * p))
                    draw_circle(x, y, cs * (0.3 + 1.6 * p))
                } else {
                    square(x, y, cs * (2.6 - 2.1 * p), tilt + p * 3.0)
                    square(x, y, cs * 0.5, tilt)
                    if p < 0.35 { draw_disc(x, y, cs * 0.35) }
                }
            }
            bind kk = kk + 1
        }
        bind j = j + 6
    }
    # legend: seven Daemon tiles
    bind lx = W * 0.68
    bind ts = min(H * 0.095, 62.0)
    bind c = 0
    while c < 7 {
        bind ly = H * 0.1 + c * (ts + H * 0.03)
        bind e = en[c]
        tile(str_slice(CAT_LET, c, 1), lx, ly, ts, 52.0 + 30.0 * e, c, sat, hs, 0.6 + e)
        col(c, sat, 68.0, hs)
        daemon_text(CAT_NAME[c], lx + ts + 14.0, ly + ts * 0.3, ts * 0.3)
        ink(62.0)
        daemon_text(fmt_mb(cats[c]) + " " + num_str(round(cats[c] / total * 1000.0) / 10.0) + "%", lx + ts + 14.0, ly + ts * 0.68, ts * 0.26)
        bind bw = W * 0.28
        bind bx = lx + ts + 14.0
        bind by = ly + ts * 0.95
        col(c, sat * 0.8, 40.0, hs)
        draw_line(bx, by, bx + bw, by)
        bind fw = bw * cats[c] / total
        col(c, sat, 60.0 + 20.0 * e, hs)
        bind u = 0.0
        while u < fw {
            draw_line(bx + u, by, bx + u + 4.0, by - 5.0)
            bind u = u + 3.0
        }
        bind c = c + 1
    }
}

# ───────────────────────────── view 3: ORBITS ────────────────────────────────
fn name_hue(name) {
    bind h = 0
    bind i = 0
    while i < str_len(name) {
        bind h = (h * 31 + str_find(DAEMON_CH, str_slice(name, i, 1)) + 1) % 7919
        bind i = i + 1
    }
    return h % 7
}

fn view_orbits(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind cx = W / 2.0
    bind cy = H / 2.0
    bind R = min(W, H) * 0.5 - 20.0
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind names = Z[Z_NAME]
    bind ws = Z[Z_WS]
    bind pv = Z[Z_PRIV]
    bind ior = Z[Z_IOR]
    bind iow = Z[Z_IOW]
    bind np = min(min(len(names), len(ior)), 22)
    bind i = 0
    while i < np {
        ink(14.0)
        draw_circle(cx, cy, R * (0.24 + 0.74 * i / max(np - 1, 1)))
        bind i = i + 1
    }
    # the sun: memory in use
    bind used = num_str(round(Z[Z_USED] * 100.0)) + "%"
    col(2, sat, 70.0, hs)
    draw_circle(cx, cy, R * 0.12)
    draw_circle(cx, cy, R * 0.1)
    daemon_text(used, cx - text_w(used, R * 0.09) / 2.0, cy, R * 0.09)
    bind k = 0
    while k < 36 {
        bind a = k * TAU / 36.0 - Z[Z_ROT] * 0.7
        pline(cx, cy, R * 0.12, a, R * 0.12 + 5.0 + 4.0 * (k % 3), a)
        bind k = k + 1
    }
    # bodies: circle = working set, square = private bytes
    bind pos = []
    bind i = 0
    while i < np {
        bind orr = R * (0.24 + 0.74 * i / max(np - 1, 1))
        bind dir = 1.0
        if i % 2 == 1 { bind dir = 0.0 - 1.0 }
        bind io = clamp(log10(1.0 + ior[i] + iow[i]) / 4.0, 0.0, 1.0)
        bind a = i * 2.399 + Z[Z_ROT] * dir * (0.8 / sqrt(1.0 + i)) * (1.0 + 2.0 * io)
        bind x = px(cx, orr, a)
        bind y = py(cy, orr, a)
        bind pos = list_push(pos, x)
        bind pos = list_push(pos, y)
        bind k = name_hue(names[i])
        bind r = clamp(4.0 + sqrt(ws[i]) * 0.3, 5.0, R * 0.11)
        col(k, sat, 48.0 + 24.0 * io, hs)
        draw_circle(x, y, r)
        draw_circle(x, y, r * 0.86)
        col(k, sat, 32.0 + 22.0 * io, hs)
        hatch_disc(x, y, r * 0.84, ANG1[k] + Z[Z_ROT] * 0.5, max(2.6, r * 0.12))
        bind sh = r * 0.7 * sqrt(clamp(pv[i] / max(ws[i], 1.0), 0.05, 1.0))
        col(k, sat, 78.0, hs)
        square(x, y, sh, t * 0.6 * dir + i)
        if r > 15.0 {
            col(k, sat, 88.0, hs)
            daemon_glyph(str_upper(str_slice(names[i], 0, 1)), x, y, r * 0.5, r * 0.6, 0.0)
        }
        if r > 11.0 {
            bind nm = str_upper(str_slice(names[i], 0, 10))
            bind m = str_len(nm)
            bind rm2 = r + 9.0
            bind da = 11.0 / rm2
            bind q = 0
            col(k, sat * 0.9, 70.0, hs)
            while q < m {
                bind ch = str_slice(nm, q, 1)
                if ch != " " { chesilin_glyph(ch, x, y, rm2, (q - (m - 1) / 2.0) * da, 9.0, 8.0) }
                bind q = q + 1
            }
        }
        bind i = i + 1
    }
    # pulses leave their process
    bind ev = Z[Z_EV]
    bind j = 0
    while j < len(ev) {
        bind pidx = ev[j + 5]
        if pidx < np {
            bind p = (t - ev[j + 2]) / 1.8
            bind x = pos[pidx * 2]
            bind y = pos[pidx * 2 + 1]
            bind k = name_hue(names[pidx])
            bind r = clamp(4.0 + sqrt(ws[pidx]) * 0.3, 5.0, R * 0.11)
            col(k, sat, (1.0 - p) * (60.0 + 38.0 * ev[j + 4]), hs)
            if ev[j] == 0 {
                draw_circle(x, y, r * (1.0 + 1.1 * p))
                draw_circle(x, y, r * (1.0 + 0.5 * p))
            } else {
                square(x, y, r * (1.9 - 0.8 * p), p * 2.5)
                square(x, y, r * (1.4 - 0.6 * p), 0.0 - p * 2.5)
            }
        }
        bind j = j + 6
    }
}

# ───────────────────────────── view 4: WEAVE ─────────────────────────────────
# Seven strings, one per category, each a sine at that category's partial ratio and
# loudness: the picture of the additive synth. The bottom trace is their sum. Notes
# (every read and write) are born at the playhead and scroll left.

fn lane_y(c, H) { return H * 0.14 + c * H * 0.095 }

fn view_weave(Z) {
    bind W = Z[Z_W]
    bind H = Z[Z_H]
    bind t = Z[Z_T]
    bind hs = Z[Z_HS]
    bind sat = Z[Z_SAT]
    bind cats = Z[Z_CATS]
    bind total = Z[Z_TOTAL]
    bind en = Z[Z_ENERGY]
    bind x0 = W * 0.12
    bind x1 = W * 0.9
    bind xph = W * 0.86
    bind sw = 8.0
    bind amp = H * 0.042
    bind c = 0
    while c < 7 {
        bind y = lane_y(c, H)
        bind e = en[c]
        bind a = amp * (0.12 + 0.88 * sqrt(cats[c] / total)) * (0.7 + 0.5 * e)
        col(c, sat * 0.6, 14.0, hs)
        draw_line(x0, y, x1, y)
        col(c, sat, 40.0 + 36.0 * e, hs)
        bind x = x0
        bind prevy = y + a * sin(0.0 - t * RATIO[c] * 1.3)
        while x < x1 {
            bind u = (x + sw - x0) / (x1 - x0)
            bind ny = y + a * sin(u * TAU * RATIO[c] * 2.0 - t * RATIO[c] * 1.3)
            draw_line(x, prevy, x + sw, ny)
            bind prevy = ny
            bind x = x + sw
        }
        tile(str_slice(CAT_LET, c, 1), W * 0.03, y - H * 0.035, H * 0.07, 40.0 + 40.0 * e, c, sat, hs, 0.3 + e)
        col(c, sat, 40.0 + 40.0 * e, hs)
        bind dr = H * 0.033
        draw_circle(W * 0.095, y, dr)
        pline(W * 0.095, y, 0.0, 0.0, dr, t * RATIO[c] * 0.6 + c)
        bind c = c + 1
    }
    # the sum of the seven strings
    bind ys = H * 0.86
    ink(18.0)
    draw_line(x0, ys, x1, ys)
    col(2, sat * 0.4, 86.0, hs)
    bind x = x0
    bind prevy = ys
    bind first = 1
    while x < x1 {
        bind u = (x - x0) / (x1 - x0)
        bind s = 0.0
        bind k = 0
        while k < 7 {
            bind s = s + sqrt(cats[k] / total) * sin(u * TAU * RATIO[k] * 2.0 - t * RATIO[k] * 1.3)
            bind k = k + 1
        }
        bind ny = ys + s * H * 0.045
        if first == 0 { draw_line(x - 3.0, prevy, x, ny) }
        bind prevy = ny
        bind first = 0
        bind x = x + 3.0
    }
    ink(70.0)
    draw_line(xph, H * 0.05, xph, H * 0.95)
    draw_line(xph + 6.0, H * 0.05, xph + 6.0, H * 0.95)
    # notes: four numbers each, kind, category, born, magnitude
    bind hist = Z[Z_HIST]
    bind i = 0
    while i < len(hist) {
        bind age = t - hist[i + 2]
        bind x = xph - age * (xph - x0) / 14.0
        bind c = hist[i + 1]
        bind y = lane_y(c, H)
        bind r = H * 0.011 + H * 0.014 * hist[i + 3]
        bind lit = 78.0 - age * 4.0
        if age < 0.4 { bind lit = 96.0 }
        col(c, sat, lit, hs)
        if hist[i] == 0 {
            draw_circle(x, y, r)
            if age < 0.9 { draw_circle(x, y, r * (1.0 + age * 3.0)) }
        } else {
            square(x, y, r, age * 0.4)
            if age < 0.9 { square(x, y, r * (1.0 + age * 2.0), age * 2.0) }
        }
        bind i = i + 4
    }
}

← Back to index