← Back to Realms

Realm

gno.land/r/moul/x/daily/collatz/v0

Overview

Realm Path
gno.land/r/moul/x/daily/collatz/v0
Exported Functions
2
State Entries
5
Source Files
2
Total Package Entries
10

Exported Functions

2 exported functions

State

5 state entries

Source Code

FILES
collatz.gno
go
1package collatz
2
3import (
4	"chain"
5	"chain/runtime/unsafe"
6	"sort"
7	"strconv"
8	"strings"
9
10	"gno.land/p/nt/avl/v0"
11)
12
13// maxIters caps the Collatz walk so a hostile / huge input cannot loop
14// forever on-chain. No known n stays below this bound and fails to reach 1.
15const maxIters = 1000000
16
17// maxSeqLen caps how many terms Render("/<n>") will print.
18const maxSeqLen = 500
19
20// Result is one recorded submission.
21type Result struct {
22	N     int64
23	Steps int
24	Peak  int64
25	Who   address
26}
27
28// results maps a decimal-string key of N -> Result. avl.Tree keeps a
29// deterministic order for iteration inside Render.
30var results avl.Tree
31
32// stopping returns the number of steps to reach 1 and the peak value seen,
33// walking the Collatz (hailstone) map. It never mutates state.
34func stopping(n int64) (steps int, peak int64) {
35	peak = n
36	cur := n
37	for cur != 1 {
38		if steps >= maxIters {
39			break
40		}
41		if cur%2 == 0 {
42			cur = cur / 2
43		} else {
44			cur = 3*cur + 1
45		}
46		if cur > peak {
47			peak = cur
48		}
49		steps++
50	}
51	return steps, peak
52}
53
54// Compute walks the hailstone sequence for n and records the result
55// attributed to the caller. It is a crossing (state-mutating) func.
56func Compute(cur realm, n int) {
57	if n <= 0 {
58		panic("n must be > 0")
59	}
60	caller := unsafe.PreviousRealm().Address()
61	nn := int64(n)
62	steps, peak := stopping(nn)
63
64	res := Result{N: nn, Steps: steps, Peak: peak, Who: caller}
65	key := strconv.FormatInt(nn, 10)
66	results.Set(key, res)
67
68	chain.Emit(
69		"Computed",
70		"n", key,
71		"steps", strconv.Itoa(steps),
72		"peak", strconv.FormatInt(peak, 10),
73		"who", caller.String(),
74	)
75}
76
77// byLongest implements sort.Interface, ordering results by steps desc,
78// then peak desc, then n asc for stability.
79type byLongest []Result
80
81func (b byLongest) Len() int      { return len(b) }
82func (b byLongest) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
83func (b byLongest) Less(i, j int) bool {
84	if b[i].Steps != b[j].Steps {
85		return b[i].Steps > b[j].Steps
86	}
87	if b[i].Peak != b[j].Peak {
88		return b[i].Peak > b[j].Peak
89	}
90	return b[i].N < b[j].N
91}
92
93// Render shows the leaderboard at root, or the full hailstone sequence for a
94// given n at path "/<n>".
95func Render(path string) string {
96	p := strings.TrimPrefix(path, "/")
97	if p == "" {
98		return renderLeaderboard()
99	}
100	return renderSequence(p)
101}
102
103func renderLeaderboard() string {
104	all := make([]Result, 0, results.Size())
105	results.Iterate("", "", func(_ string, v interface{}) bool {
106		all = append(all, v.(Result))
107		return false
108	})
109
110	var sb strings.Builder
111	sb.WriteString("# Collatz Explorer\n\n")
112	sb.WriteString("Hailstone-sequence explorer. Call `Compute(n)` to record a run, ")
113	sb.WriteString("or view a full sequence at `/<n>`.\n\n")
114
115	if len(all) == 0 {
116		sb.WriteString("_No sequences submitted yet._\n")
117		return sb.String()
118	}
119
120	sort.Stable(byLongest(all))
121
122	sb.WriteString("## Leaderboard — longest sequences\n\n")
123	sb.WriteString("| # | n | steps | peak | who |\n")
124	sb.WriteString("|---|---|-------|------|-----|\n")
125	limit := len(all)
126	if limit > 20 {
127		limit = 20
128	}
129	for i := 0; i < limit; i++ {
130		r := all[i]
131		sb.WriteString("| " + strconv.Itoa(i+1) + " | ")
132		sb.WriteString("[" + strconv.FormatInt(r.N, 10) + "](/r:" + strconv.FormatInt(r.N, 10) + ") | ")
133		sb.WriteString(strconv.Itoa(r.Steps) + " | ")
134		sb.WriteString(strconv.FormatInt(r.Peak, 10) + " | ")
135		sb.WriteString(r.Who.String() + " |\n")
136	}
137	sb.WriteString("\n_" + strconv.Itoa(len(all)) + " sequence(s) recorded._\n")
138	return sb.String()
139}
140
141func renderSequence(p string) string {
142	n, err := strconv.ParseInt(p, 10, 64)
143	var sb strings.Builder
144	if err != nil || n <= 0 {
145		sb.WriteString("# Invalid n\n\n`" + p + "` is not a positive integer.\n")
146		return sb.String()
147	}
148
149	sb.WriteString("# Hailstone sequence for " + strconv.FormatInt(n, 10) + "\n\n")
150
151	seq := make([]int64, 0, 64)
152	cur := n
153	var peak int64 = n
154	truncated := false
155	for {
156		seq = append(seq, cur)
157		if cur == 1 {
158			break
159		}
160		if len(seq) >= maxSeqLen {
161			truncated = true
162			break
163		}
164		if cur%2 == 0 {
165			cur = cur / 2
166		} else {
167			cur = 3*cur + 1
168		}
169		if cur > peak {
170			peak = cur
171		}
172	}
173
174	parts := make([]string, len(seq))
175	for i, v := range seq {
176		parts[i] = strconv.FormatInt(v, 10)
177	}
178	sb.WriteString(strings.Join(parts, " → "))
179	sb.WriteString("\n\n")
180	sb.WriteString("- steps: " + strconv.Itoa(len(seq)-1) + "\n")
181	sb.WriteString("- peak: " + strconv.FormatInt(peak, 10) + "\n")
182	if truncated {
183		sb.WriteString("\n_Sequence truncated at " + strconv.Itoa(maxSeqLen) + " terms._\n")
184	}
185	return sb.String()
186}
187

Raw Package Data

Raw JSON data