import Batteries /-! # Standard Library and Batteries Code examples for IO operations, Batteries, and the package ecosystem. -/ namespace ZeroToQED.StdLibrary -- ANCHOR: io_basics -- Basic IO operations def ioDemo : IO Unit := do -- Print to stdout IO.println "Hello, world!" IO.print "No newline" IO.println "" -- newline -- Environment variables let path ← IO.getEnv "PATH" IO.println s!"PATH exists: {path.isSome}" -- Current time (milliseconds since epoch) let now ← IO.monoMsNow IO.println s!"Timestamp: {now}" -- Random numbers let rand ← IO.rand 1 100 IO.println s!"Random 1-100: {rand}" -- ANCHOR_END: io_basics -- ANCHOR: file_io -- File operations def fileDemo : IO Unit := do let testFile := "test_output.txt" -- Write to file IO.FS.writeFile testFile "Line 1\nLine 2\nLine 3\n" -- Read entire file let contents ← IO.FS.readFile testFile IO.println s!"Contents:\n{contents}" -- Read as lines let lines ← IO.FS.lines testFile IO.println s!"Number of lines: {lines.size}" for line in lines do IO.println s!" > {line}" -- Clean up IO.FS.removeFile testFile -- ANCHOR_END: file_io -- ANCHOR: directory_io -- Directory operations def directoryDemo : IO Unit := do -- Current working directory let cwd ← IO.currentDir IO.println s!"Current directory: {cwd}" -- List directory contents let entries ← System.FilePath.readDir "." IO.println s!"Files in current directory: {entries.size}" -- Print first few entries for entry in entries.toList.take 5 do IO.println s!" {entry.fileName}" -- ANCHOR_END: directory_io -- ANCHOR: process_io -- Running external processes def processDemo : IO Unit := do -- Run a command and capture output let output ← IO.Process.output { cmd := "echo" args := #["Hello from subprocess"] } IO.println s!"Exit code: {output.exitCode}" IO.println s!"stdout: {output.stdout}" -- Check if command succeeded if output.exitCode == 0 then IO.println "Command succeeded" else IO.println s!"Command failed: {output.stderr}" -- ANCHOR_END: process_io -- ANCHOR: batteries_heap -- BinaryHeap: priority queue with O(log n) push/pop def heapDemo : IO Unit := do -- Max-heap (largest first with >) let heap := Batteries.BinaryHeap.empty (α := Nat) (lt := (· > ·)) |>.insert 5 |>.insert 1 |>.insert 3 |>.insert 9 IO.println s!"heap max: {heap.max}" -- some 9 -- Pop the max element let (maxVal, heap') := heap.extractMax IO.println s!"extracted: {maxVal}" -- some 9 IO.println s!"new max: {heap'.max}" -- some 5 -- ANCHOR_END: batteries_heap -- ANCHOR: batteries_rbmap -- RBMap: ordered map with O(log n) operations def rbmapDemo : IO Unit := do -- Create a map with String keys, Nat values let scores : Batteries.RBMap String Nat compare := Batteries.RBMap.empty |>.insert "alice" 95 |>.insert "bob" 87 |>.insert "carol" 92 -- Lookup let aliceScore := scores.find? "alice" let daveScore := scores.find? "dave" IO.println s!"alice: {aliceScore}" -- some 95 IO.println s!"dave: {daveScore}" -- none -- Convert to list for display (sorted by key) IO.println s!"all: {scores.toList}" -- ANCHOR_END: batteries_rbmap -- ANCHOR: batteries_unionfind -- UnionFind: disjoint set with near O(1) union/find def unionFindDemo : IO Unit := do -- Create structure and add 5 elements (indices 0-4) let uf := Batteries.UnionFind.empty |>.push |>.push |>.push |>.push |>.push -- Union 0 with 1, and 2 with 3 let uf := uf.union! 0 1 let uf := uf.union! 2 3 -- Check if elements are in same set (same root) let root0 := uf.rootD 0 let root1 := uf.rootD 1 let root2 := uf.rootD 2 let root4 := uf.rootD 4 IO.println s!"0 and 1 same set: {root0 == root1}" -- true IO.println s!"0 and 2 same set: {root0 == root2}" -- false IO.println s!"4 alone: {root4 == 4}" -- true -- ANCHOR_END: batteries_unionfind -- ANCHOR: batteries_dlist -- DList: difference list with O(1) append def dlistDemo : IO Unit := do -- Build a list by repeated appending -- With regular List, this would be O(n^2), with DList it's O(n) let d1 : Batteries.DList Nat := Batteries.DList.singleton 1 let d2 := d1.push 2 let d3 := d2.push 3 let d4 := d3.append (Batteries.DList.ofList [4, 5, 6]) -- Convert back to List when done building let result := d4.toList IO.println s!"result: {result}" -- [1, 2, 3, 4, 5, 6] -- ANCHOR_END: batteries_dlist -- ANCHOR: batteries_list_array -- Batteries extends List and Array with useful functions def batteriesListDemo : IO Unit := do let nums := [1, 2, 3, 4, 5] -- Chunking let chunks := nums.toChunks 2 IO.println s!"toChunks 2: {chunks}" -- [[1, 2], [3, 4], [5]] -- Rotating let rotL := nums.rotateLeft 2 let rotR := nums.rotateRight 2 IO.println s!"rotateLeft 2: {rotL}" -- [3, 4, 5, 1, 2] IO.println s!"rotateRight 2: {rotR}" -- [4, 5, 1, 2, 3] -- Partitioning let (small, big) := nums.partition (· ≤ 3) IO.println s!"partition: small={small}, big={big}" -- Erasure (remove first occurrence) let erased := nums.erase 3 IO.println s!"erase 3: {erased}" -- [1, 2, 4, 5] -- ANCHOR_END: batteries_list_array -- ANCHOR: std_hashmap -- HashMap: O(1) average insert/lookup def hashmapDemo : IO Unit := do -- Create a HashMap from key-value pairs let mut scores : Std.HashMap String Nat := {} scores := scores.insert "alice" 95 scores := scores.insert "bob" 87 scores := scores.insert "carol" 92 -- Lookup with get? let aliceScore := scores.get? "alice" IO.println s!"alice: {aliceScore}" -- some 95 -- Check membership IO.println s!"contains bob: {scores.contains "bob"}" -- true -- Get with default let daveScore := scores.getD "dave" 0 IO.println s!"dave (default 0): {daveScore}" -- 0 -- Iterate over entries IO.println "All scores:" for (name, score) in scores do IO.println s!" {name}: {score}" -- ANCHOR_END: std_hashmap -- ANCHOR: std_hashset -- HashSet: O(1) average membership testing def hashsetDemo : IO Unit := do -- Create a HashSet let mut seen : Std.HashSet String := {} seen := seen.insert "apple" seen := seen.insert "banana" seen := seen.insert "cherry" IO.println s!"contains apple: {seen.contains "apple"}" -- true IO.println s!"contains grape: {seen.contains "grape"}" -- false IO.println s!"size: {seen.size}" -- 3 -- Set operations let more : Std.HashSet String := Std.HashSet.ofList ["banana", "date", "elderberry"] let combined := seen.union more IO.println s!"union size: {combined.size}" -- 5 -- ANCHOR_END: std_hashset -- ANCHOR: std_treemap -- TreeMap: ordered map with O(log n) operations def treemapDemo : IO Unit := do -- TreeMap keeps keys sorted let mut prices : Std.TreeMap String Nat := {} prices := prices.insert "banana" 120 prices := prices.insert "apple" 100 prices := prices.insert "cherry" 300 -- Iteration is in sorted order IO.println "Prices (sorted by name):" for (fruit, price) in prices do IO.println s!" {fruit}: {price}" -- Size and membership IO.println s!"size: {prices.size}" IO.println s!"contains apple: {prices.contains "apple"}" -- ANCHOR_END: std_treemap -- ANCHOR: std_time -- Time: dates, times, and durations def timeDemo : IO Unit := do -- Get current time let now ← IO.monoNanosNow IO.println s!"Monotonic nanoseconds: {now}" -- Durations let oneSecond := 1000000000 -- nanoseconds let elapsed := now % oneSecond IO.println s!"Nanoseconds into current second: {elapsed}" -- For wall-clock time, use IO.getNumHeartbeats or external libraries let heartbeats ← IO.getNumHeartbeats IO.println s!"Heartbeats: {heartbeats}" -- ANCHOR_END: std_time -- ANCHOR: std_parsec -- Parsec: parser combinators for parsing text open Std.Internal.Parsec String in def parsecDemo : IO Unit := do -- Parser for a natural number let parseNat : Parser Nat := digits -- Parser for a word (one or more ASCII letters) let parseWord : Parser String := many1Chars asciiLetter -- Parser for comma-separated numbers let parseNumList : Parser (List Nat) := do let first ← parseNat let rest ← many (skipChar ',' *> parseNat) pure (first :: rest.toList) -- Run parsers match parseNat.run "12345" with | .ok n => IO.println s!"Number: {n}" -- 12345 | .error e => IO.println s!"Error: {e}" match parseWord.run "hello123" with | .ok s => IO.println s!"Word: {s}" -- "hello" | .error e => IO.println s!"Error: {e}" match parseNumList.run "1,23,456" with | .ok ns => IO.println s!"List: {ns}" -- [1, 23, 456] | .error e => IO.println s!"Error: {e}" -- ANCHOR_END: std_parsec -- ANCHOR: practical_example -- Practical example: word frequency counter def countWords (text : String) : Std.HashMap String Nat := let words := text.toLower.splitOn " " |>.map (fun w => w.toList.filter Char.isAlpha |> String.ofList) |>.filter (!·.isEmpty) words.foldl (fun map word => map.insert word ((map.get? word).getD 0 + 1) ) {} def wordFreqDemo : IO Unit := do let text := "The quick brown fox jumps over the lazy dog The dog barks" let freq := countWords text IO.println "Word frequencies:" let sorted := freq.toList.toArray |>.qsort (fun a b => a.2 > b.2) for (word, count) in sorted do IO.println s!" {word}: {count}" -- ANCHOR_END: practical_example def main : IO Unit := do IO.println "=== IO Basics ===" ioDemo IO.println "=== File IO ===" fileDemo IO.println "=== Directory IO ===" directoryDemo IO.println "=== Process IO ===" processDemo IO.println "=== HashMap ===" hashmapDemo IO.println "=== HashSet ===" hashsetDemo IO.println "=== TreeMap ===" treemapDemo IO.println "=== Time ===" timeDemo IO.println "=== Parsec ===" parsecDemo IO.println "=== BinaryHeap ===" heapDemo IO.println "=== RBMap ===" rbmapDemo IO.println "=== UnionFind ===" unionFindDemo IO.println "=== DList ===" dlistDemo IO.println "=== List Extensions ===" batteriesListDemo IO.println "=== Word Frequency ===" wordFreqDemo end ZeroToQED.StdLibrary