Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions 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}