1// Package sievedemo is a small gnoweb demo of the Sieve of Eratosthenes2// provided by the [p/moul/x/daily/sieve](/p/moul/x/daily/sieve/v0) library: it3// renders the primes up to MaxN, the first 100 primes, and an interactive4// "primes up to n" grid.5//6// It contains no sieve logic of its own — everything comes from7// `sieve.PrimesUpTo` and `sieve.MaxN`.8package sievedemo910import (11 "strconv"12 "strings"1314 "gno.land/p/moul/x/daily/sieve/v0"15)1617// Render renders the sieve for gnoweb.18//19// Render("") / Render("/") -> first 100 primes + total count up to MaxN20// Render("/<n>") -> every prime <= n, laid out as a grid21func Render(path string) string {22 n, hasN := parseN(path)2324 var b strings.Builder25 b.WriteString("# Sieve of Eratosthenes\n\n")26 b.WriteString("A deterministic on-chain port of Go's classic concurrent prime-sieve example, ")27 b.WriteString("demoing the [`p/moul/x/daily/sieve`](/p/moul/x/daily/sieve/v0) library.\n\n")2829 if !hasN {30 primes := sieve.PrimesUpTo(sieve.MaxN)31 b.WriteString("There are **")32 b.WriteString(strconv.Itoa(len(primes)))33 b.WriteString("** primes up to ")34 b.WriteString(strconv.Itoa(sieve.MaxN))35 b.WriteString(".\n\n")36 b.WriteString("## First 100 primes\n\n")37 first := primes38 if len(first) > 100 {39 first = first[:100]40 }41 b.WriteString(grid(first, 10))42 b.WriteString("\n> Try `/500` or `/1000` to sieve up to any n (max ")43 b.WriteString(strconv.Itoa(sieve.MaxN))44 b.WriteString(").\n")45 return b.String()46 }4748 if n < 2 {49 b.WriteString("No primes <= ")50 b.WriteString(strconv.Itoa(n))51 b.WriteString(".\n")52 return b.String()53 }5455 clamped := n56 if clamped > sieve.MaxN {57 clamped = sieve.MaxN58 }59 primes := sieve.PrimesUpTo(clamped)60 b.WriteString("## Primes up to ")61 b.WriteString(strconv.Itoa(clamped))62 if clamped != n {63 b.WriteString(" (clamped from ")64 b.WriteString(strconv.Itoa(n))65 b.WriteString(")")66 }67 b.WriteString("\n\n")68 b.WriteString("Count: **")69 b.WriteString(strconv.Itoa(len(primes)))70 b.WriteString("**\n\n")71 b.WriteString(grid(primes, 10))72 return b.String()73}7475// parseN extracts n from a Render path like "/1000". Returns hasN=false for76// the empty/root path.77func parseN(path string) (int, bool) {78 s := strings.TrimSpace(path)79 s = strings.TrimPrefix(s, "/")80 if s == "" {81 return 0, false82 }83 // keep only the first segment84 if i := strings.IndexByte(s, '/'); i >= 0 {85 s = s[:i]86 }87 v, err := strconv.Atoi(s)88 if err != nil {89 return 0, false90 }91 return v, true92}9394// grid formats primes into a Markdown table with `cols` columns per row.95func grid(primes []int, cols int) string {96 if len(primes) == 0 {97 return "_none_\n"98 }99 if cols < 1 {100 cols = 1101 }102103 var b strings.Builder104 // header + separator so gnoweb renders it as a table105 b.WriteString("|")106 for c := 0; c < cols; c++ {107 b.WriteString(" · |")108 }109 b.WriteString("\n|")110 for c := 0; c < cols; c++ {111 b.WriteString("---|")112 }113 b.WriteString("\n")114115 for i := 0; i < len(primes); i += cols {116 b.WriteString("|")117 for c := 0; c < cols; c++ {118 b.WriteString(" ")119 if i+c < len(primes) {120 b.WriteString(strconv.Itoa(primes[i+c]))121 }122 b.WriteString(" |")123 }124 b.WriteString("\n")125 }126 return b.String()127}128Render(path string) string
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.