Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

sievedemo.gno

3.14 Kb · 127 lines
  1// Package sievedemo is a small gnoweb demo of the Sieve of Eratosthenes
  2// provided by the [p/moul/x/daily/sieve](/p/moul/x/daily/sieve/v0) library: it
  3// renders the primes up to MaxN, the first 100 primes, and an interactive
  4// "primes up to n" grid.
  5//
  6// It contains no sieve logic of its own — everything comes from
  7// `sieve.PrimesUpTo` and `sieve.MaxN`.
  8package sievedemo
  9
 10import (
 11	"strconv"
 12	"strings"
 13
 14	"gno.land/p/moul/x/daily/sieve/v0"
 15)
 16
 17// Render renders the sieve for gnoweb.
 18//
 19//	Render("")      / Render("/") -> first 100 primes + total count up to MaxN
 20//	Render("/<n>")               -> every prime <= n, laid out as a grid
 21func Render(path string) string {
 22	n, hasN := parseN(path)
 23
 24	var b strings.Builder
 25	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")
 28
 29	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 := primes
 38		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	}
 47
 48	if n < 2 {
 49		b.WriteString("No primes <= ")
 50		b.WriteString(strconv.Itoa(n))
 51		b.WriteString(".\n")
 52		return b.String()
 53	}
 54
 55	clamped := n
 56	if clamped > sieve.MaxN {
 57		clamped = sieve.MaxN
 58	}
 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}
 74
 75// parseN extracts n from a Render path like "/1000". Returns hasN=false for
 76// 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, false
 82	}
 83	// keep only the first segment
 84	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, false
 90	}
 91	return v, true
 92}
 93
 94// 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 = 1
101	}
102
103	var b strings.Builder
104	// header + separator so gnoweb renders it as a table
105	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")
114
115	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}