Compare commits

...

85 Commits

Author SHA1 Message Date
ea9cb1f846 Revert "A bit more silly extra credit for ex12"
This reverts commit 58e734c60f.
2024-04-18 19:45:56 -04:00
58e734c60f A bit more silly extra credit for ex12 2024-04-18 19:45:38 -04:00
df5d4f5a1f Kinda extra credit ex12 2024-04-18 18:49:10 -04:00
32e70e96ce Up through lcthw ex12 2024-04-18 18:28:12 -04:00
8af4f619f4 Revert "Trying and failing to break ex11.c"
This reverts commit 52a5ea57e1.
2024-04-17 08:29:40 -04:00
52a5ea57e1 Trying and failing to break ex11.c 2024-04-17 08:29:30 -04:00
1a894440f9 Up through lcthw 11 2024-04-17 08:22:20 -04:00
353b77271d Modifications to ex10 2024-04-16 21:10:49 -04:00
8f9842ddfb Up through lcthw 10 2024-04-16 21:10:41 -04:00
94cd12ac82 While backward 2024-04-16 20:47:34 -04:00
726566bb23 Up through lcthw ex9 2024-04-16 20:46:08 -04:00
7e07054a70 Having 2024-04-16 20:45:47 -04:00
af95125894 Up through ex8 2024-04-16 08:17:54 -04:00
7da7228911 Up through lcthw ex7 2024-04-16 08:03:12 -04:00
88133117ab A more makey makefile 2024-04-15 19:59:15 -04:00
c8ac7ac727 Much cleanup to better match lesson text 2024-04-15 15:28:45 -04:00
a135c338ea LCTHW ex3 2024-04-15 15:22:58 -04:00
0fd7b5873d LCTHW ex 2ish 2024-04-15 15:16:14 -04:00
e28293e5bd Back to having fun with LCTHW 2024-04-15 14:30:58 -04:00
58002830a8 Swinging the pendulum back to extremely basic 2024-04-15 09:55:20 -04:00
8532dd4657 Heron something what the heck 2024-04-14 22:17:51 -04:00
f6ac67a6a6 Too many more things about ModernC 2024-04-14 22:08:23 -04:00
8424a6836a Doing my part for the flame war 2024-04-13 13:32:43 -04:00
8e2f5c1ff0 The "bad" getting started 2024-04-13 13:25:31 -04:00
9183869a50 Begin working through Modern C 2018-ish edition 2024-04-13 12:07:04 -04:00
1ed7fb7d33 This home page is _personal_ 2024-03-01 22:49:44 -05:00
a498ff949c Oh right this thing 2024-03-01 19:48:32 -05:00
ad65630b3f Wasn't I doing something with the UMN Kauffman course 2024-03-01 19:46:36 -05:00
9109e3a3dc Booping around with kauffman assembly basics
https://www-users.cse.umn.edu/~kauffman/2021/06-assembly-basics.pdf
2023-11-08 12:42:46 -05:00
9d36627e87 Cloned undirected graph 2023-11-01 00:12:36 -04:00
db38eeada9 One more thought about max sub-array 2023-10-31 08:00:18 -04:00
cb68a35b1b Sliding window max (peeked a bit) 2023-10-31 00:58:52 -04:00
01a189c5ee Yet Another Off By One Error 2023-10-28 08:24:17 -04:00
3580198513 Max sub-array slidey window 2023-10-28 08:21:39 -04:00
efb8f453ae Weird copying linked list-ish thing with random 2023-10-27 17:33:22 -04:00
5df7489f27 Factorial trailing zeroes thing 2023-10-27 16:12:17 -04:00
efc1c8f029 Fully un-busted yet slow Trie 2023-10-27 08:19:03 -04:00
43a2e51712 Less busted trie 2023-10-26 20:44:05 -04:00
88e4e319d3 Busted trie impl 2023-10-26 19:59:11 -04:00
c274f59f58 Do a better RandomizedSet 2023-10-26 03:58:50 -04:00
c8a4928ee8 a rushed RandomizedSet impl 2023-10-25 20:15:43 -04:00
6d523ad7c1 h-index cleanups 2023-10-25 19:19:00 -04:00
d9393152c5 Something like h-index? 2023-10-25 19:18:05 -04:00
2f3ce704b2 Jump game finally (?) but differently than solutions 2023-10-25 16:51:35 -04:00
54a7a2340c Trying jump game again 2023-10-24 19:46:01 -04:00
8cc2e18965 Move roman bits 2023-10-24 15:48:15 -04:00
ffb16efcdc Cheating with binary tree goop 2023-10-24 15:42:42 -04:00
10ec594031 Trying to understand binary tree from array 2023-10-24 08:12:35 -04:00
cee338520e Sum binary tree path ints 2023-10-21 16:34:42 -04:00
e92d117a41 Return the correct right-connected binary tree root 2023-10-21 16:19:13 -04:00
dc9c3fd54d Binary tree right join goop 2023-10-21 15:45:16 -04:00
a8d82b180a Sorting linked list with mutation (oh no) 2023-10-21 08:41:41 -04:00
337a795ad0 Linked list sorting (?) 2023-10-21 08:23:14 -04:00
3f369e1e70 Correct min stack (?) 2023-10-21 07:25:38 -04:00
a3819c9b26 A poorly-made min stack 2023-10-21 07:18:56 -04:00
345464d0d4 Add a guard rail around Roman.i2r 2023-10-20 10:51:29 -04:00
5b55118373 More fun with roman numbers 2023-10-20 10:38:20 -04:00
accf0d0c2b Linty bits 2023-10-20 08:22:16 -04:00
b672131cfc Rearranging a bit 2023-10-20 08:11:07 -04:00
d03225edce Bisecting and matrix goop 2023-10-20 07:57:25 -04:00
8434bf7025 Goop while suffering through leetcode things 2023-10-19 11:23:24 -04:00
087355502d A few more RBE things 2023-10-19 11:08:38 -04:00
0e6448df23 Remove the example mix project 2023-10-08 12:50:13 -04:00
4da817996d Doing an example mix project 2023-10-08 12:49:42 -04:00
7e0aa3b86b fn closures in RBE 2023-10-01 06:01:54 -04:00
aa83db6f7a fn closure in RBE 2023-09-30 20:17:22 -04:00
9d4f5b661b Up through fn methods in RBE 2023-09-30 20:05:41 -04:00
b94fb3b31e Remaining flow control in RBE 2023-09-30 19:51:09 -04:00
dacbddca6e Some if-let in RBE 2023-09-30 19:39:55 -04:00
6e50ec65f8 Flow control match binding in RBE 2023-09-29 16:52:18 -04:00
ca812add16 Much more flow control destructuring in RBE 2023-09-27 23:16:10 -04:00
460741740b Flow control for in RBE 2023-09-26 11:03:55 -04:00
7204f08177 Expressions and starting flow control in RBE 2023-09-26 06:43:22 -04:00
505e04613f Up through conversion in RBE 2023-09-26 06:07:51 -04:00
7f4f627769 Much renaming and refreshing of RBE 2023-09-24 20:46:41 -04:00
dc8045a20e Trivial goop while refreshing on RBE 2023-09-24 10:57:16 -04:00
41f53c34c0 Making some arduino-shaped messes 2023-01-07 18:44:31 -05:00
6c0083e094 Sudoku through the end of chapter 8 2022-11-26 16:09:32 -05:00
7f2f579241 Sudoku up through chapter 7 2022-11-26 09:42:41 -05:00
7d2ac20ce1 Beginning of sudoku chapter 7 with SDM loading 2022-11-25 15:37:57 -05:00
8f38a3b271 Sudoku up through chapter 5 2022-11-25 12:20:01 -05:00
96f5f85a27 Piston Sudoku up through 3rd chapter 2022-11-24 18:56:18 -05:00
6cf80e09b1 Piston tutorial getting started woop! 2022-11-24 18:25:07 -05:00
30ed115ed0 Moving argh to separate repo 2022-06-06 18:50:25 -04:00
8376608a1e Merge pull request 'arghing' (#1) from arghing into main
Reviewed-on: #1
2022-06-06 22:44:24 +00:00
149 changed files with 7760 additions and 2258 deletions

11
.editorconfig Normal file
View File

@@ -0,0 +1,11 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 90
[{*.c,*.go}]
indent_style = tab
indent_size = 4

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@
**/target/ **/target/
/hello_world/main /hello_world/main
/aoc*/**/input /aoc*/**/input
/arduino/build-*/

2
arduino/.envrc Normal file
View File

@@ -0,0 +1,2 @@
export ARDMK_DIR=/usr/share/arduino
export ARDMK_VENDOR=archlinux-arduino

3
arduino/Makefile Normal file
View File

@@ -0,0 +1,3 @@
BOARD_TAG = uno
include $(ARDMK_DIR)/Arduino.mk

31
arduino/sos.ino Normal file
View File

@@ -0,0 +1,31 @@
#define DIT_DURATION_MS 88
#define LETTER_PAUSE_MS 1000
void setup() {
pinMode(13, OUTPUT);
}
void dit() {
digitalWrite(13, HIGH);
delay(DIT_DURATION_MS);
digitalWrite(13, LOW);
delay(DIT_DURATION_MS);
}
void dah() {
digitalWrite(13, HIGH);
delay(DIT_DURATION_MS * 3);
digitalWrite(13, LOW);
delay(DIT_DURATION_MS);
}
void loop() {
dit(); dit(); dit();
delay(LETTER_PAUSE_MS);
dah();
delay(LETTER_PAUSE_MS);
dit(); dit(); dit();
delay(LETTER_PAUSE_MS);
}

View File

@@ -1,4 +0,0 @@
# argh command line parser
> NOTE: much of this is lifted from
> https://blog.gopheracademy.com/advent-2014/parsers-lexers/

View File

@@ -1,34 +0,0 @@
package argh
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
)
var (
tracingEnabled = os.Getenv("ARGH_TRACING") == "enabled"
traceLogger *log.Logger
)
func init() {
if !tracingEnabled {
return
}
traceLogger = log.New(os.Stderr, "ARGH TRACING: ", 0)
}
func tracef(format string, v ...any) {
if !tracingEnabled {
return
}
if _, file, line, ok := runtime.Caller(1); ok {
format = fmt.Sprintf("%v:%v ", filepath.Base(file), line) + format
}
traceLogger.Printf(format, v...)
}

View File

@@ -1,46 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"git.meatballhat.com/x/box-o-sand/argh"
"github.com/davecgh/go-spew/spew"
)
func main() {
asJSON := os.Getenv("ARGH_OUTPUT_JSON") == "enabled"
log.SetFlags(0)
pt, err := argh.ParseArgs(os.Args, argh.NewParserConfig(
&argh.CommandConfig{
NValue: argh.OneOrMoreValue,
ValueNames: []string{"topping"},
Flags: &argh.Flags{
Automatic: true,
},
},
nil,
))
if err != nil {
log.Fatal(err)
}
ast := argh.NewQuerier(pt.Nodes).AST()
if asJSON {
b, err := json.MarshalIndent(ast, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
return
}
spew.Dump(ast)
}

View File

@@ -1,12 +0,0 @@
module git.meatballhat.com/x/box-o-sand/argh
go 1.18
require github.com/pkg/errors v0.9.1
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.7.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

View File

@@ -1,12 +0,0 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,46 +0,0 @@
package argh
type Node interface{}
type TypedNode struct {
Type string
Node Node
}
type PassthroughArgs struct {
Nodes []Node
}
type CompoundShortFlag struct {
Nodes []Node
}
type Ident struct {
Literal string
}
type BadArg struct {
Literal string
From Pos
To Pos
}
type Command struct {
Name string
Values map[string]string
Nodes []Node
}
type Flag struct {
Name string
Values map[string]string
Nodes []Node
}
type StdinFlag struct{}
type StopFlag struct{}
type ArgDelimiter struct{}
type Assign struct{}

View File

@@ -1,26 +0,0 @@
// Code generated by "stringer -type NValue"; DO NOT EDIT.
package argh
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[OneOrMoreValue - -2]
_ = x[ZeroOrMoreValue - -1]
_ = x[ZeroValue-0]
}
const _NValue_name = "OneOrMoreValueZeroOrMoreValueZeroValue"
var _NValue_index = [...]uint8{0, 14, 29, 38}
func (i NValue) String() string {
i -= -2
if i < 0 || i >= NValue(len(_NValue_index)-1) {
return "NValue(" + strconv.FormatInt(int64(i+-2), 10) + ")"
}
return _NValue_name[_NValue_index[i]:_NValue_index[i+1]]
}

View File

@@ -1,346 +0,0 @@
package argh
import (
"fmt"
"io"
"strings"
)
type parser struct {
s *Scanner
cfg *ParserConfig
errors ParserErrorList
tok Token
lit string
pos Pos
buffered bool
}
type ParseTree struct {
Nodes []Node `json:"nodes"`
}
func ParseArgs(args []string, pCfg *ParserConfig) (*ParseTree, error) {
p := &parser{}
p.init(
strings.NewReader(strings.Join(args, string(nul))),
pCfg,
)
tracef("ParseArgs(...) parser=%+#v", p)
return p.parseArgs()
}
func (p *parser) addError(msg string) {
p.errors.Add(Position{Column: int(p.pos)}, msg)
}
func (p *parser) init(r io.Reader, pCfg *ParserConfig) {
p.errors = ParserErrorList{}
if pCfg == nil {
pCfg = POSIXyParserConfig
}
p.cfg = pCfg
p.s = NewScanner(r, pCfg.ScannerConfig)
p.next()
}
func (p *parser) parseArgs() (*ParseTree, error) {
if p.errors.Len() != 0 {
tracef("parseArgs() bailing due to initial error")
return nil, p.errors.Err()
}
tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog)
prog := p.parseCommand(&p.cfg.Prog)
tracef("parseArgs() top level node is %T", prog)
nodes := []Node{prog}
if v := p.parsePassthrough(); v != nil {
tracef("parseArgs() appending passthrough argument %v", v)
nodes = append(nodes, v)
}
tracef("parseArgs() returning ParseTree")
return &ParseTree{Nodes: nodes}, p.errors.Err()
}
func (p *parser) next() {
tracef("next() before scan: %v %q %v", p.tok, p.lit, p.pos)
p.tok, p.lit, p.pos = p.s.Scan()
tracef("next() after scan: %v %q %v", p.tok, p.lit, p.pos)
}
func (p *parser) parseCommand(cCfg *CommandConfig) Node {
tracef("parseCommand(%+#v)", cCfg)
node := &Command{
Name: p.lit,
}
values := map[string]string{}
nodes := []Node{}
identIndex := 0
for i := 0; p.tok != EOL; i++ {
if !p.buffered {
tracef("parseCommand(...) buffered=false; scanning next")
p.next()
}
p.buffered = false
tracef("parseCommand(...) for=%d values=%+#v", i, values)
tracef("parseCommand(...) for=%d nodes=%+#v", i, nodes)
tracef("parseCommand(...) for=%d tok=%s lit=%q pos=%v", i, p.tok, p.lit, p.pos)
tracef("parseCommand(...) cCfg=%+#v", cCfg)
if subCfg, ok := cCfg.GetCommandConfig(p.lit); ok {
subCommand := p.lit
nodes = append(nodes, p.parseCommand(&subCfg))
tracef("parseCommand(...) breaking after sub-command=%v", subCommand)
break
}
switch p.tok {
case ARG_DELIMITER:
tracef("parseCommand(...) handling %s", p.tok)
nodes = append(nodes, &ArgDelimiter{})
continue
case IDENT, STDIN_FLAG:
tracef("parseCommand(...) handling %s", p.tok)
if cCfg.NValue.Contains(identIndex) {
name := fmt.Sprintf("%d", identIndex)
tracef("parseCommand(...) checking for name of identIndex=%d", identIndex)
if len(cCfg.ValueNames) > identIndex {
name = cCfg.ValueNames[identIndex]
tracef("parseCommand(...) setting name=%s from config value names", name)
} else if len(cCfg.ValueNames) == 1 && (cCfg.NValue == OneOrMoreValue || cCfg.NValue == ZeroOrMoreValue) {
name = fmt.Sprintf("%s.%d", cCfg.ValueNames[0], identIndex)
tracef("parseCommand(...) setting name=%s from repeating value name", name)
}
values[name] = p.lit
}
if p.tok == STDIN_FLAG {
nodes = append(nodes, &StdinFlag{})
} else {
nodes = append(nodes, &Ident{Literal: p.lit})
}
identIndex++
case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG:
tok := p.tok
flagNode := p.parseFlag(cCfg.Flags)
tracef("parseCommand(...) appending %s node=%+#v", tok, flagNode)
nodes = append(nodes, flagNode)
case ASSIGN:
tracef("parseCommand(...) error on bare %s", p.tok)
p.addError("invalid bare assignment")
break
default:
tracef("parseCommand(...) breaking on %s", p.tok)
break
}
}
if len(nodes) > 0 {
node.Nodes = nodes
}
if len(values) > 0 {
node.Values = values
}
tracef("parseCommand(...) returning node=%+#v", node)
return node
}
func (p *parser) parseIdent() Node {
node := &Ident{Literal: p.lit}
return node
}
func (p *parser) parseFlag(flags *Flags) Node {
switch p.tok {
case SHORT_FLAG:
tracef("parseFlag(...) parsing short flag with config=%+#v", flags)
return p.parseShortFlag(flags)
case LONG_FLAG:
tracef("parseFlag(...) parsing long flag with config=%+#v", flags)
return p.parseLongFlag(flags)
case COMPOUND_SHORT_FLAG:
tracef("parseFlag(...) parsing compound short flag with config=%+#v", flags)
return p.parseCompoundShortFlag(flags)
}
panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok))
}
func (p *parser) parseShortFlag(flags *Flags) Node {
node := &Flag{Name: string(p.lit[1])}
flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
return node
}
return p.parseConfiguredFlag(node, flCfg)
}
func (p *parser) parseLongFlag(flags *Flags) Node {
node := &Flag{Name: string(p.lit[2:])}
flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
return node
}
return p.parseConfiguredFlag(node, flCfg)
}
func (p *parser) parseCompoundShortFlag(flags *Flags) Node {
flagNodes := []Node{}
withoutFlagPrefix := p.lit[1:]
for i, r := range withoutFlagPrefix {
node := &Flag{Name: string(r)}
if i == len(withoutFlagPrefix)-1 {
flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
continue
}
flagNodes = append(flagNodes, p.parseConfiguredFlag(node, flCfg))
continue
}
flagNodes = append(flagNodes, node)
}
return &CompoundShortFlag{Nodes: flagNodes}
}
func (p *parser) parseConfiguredFlag(node *Flag, flCfg FlagConfig) Node {
values := map[string]string{}
nodes := []Node{}
identIndex := 0
for i := 0; p.tok != EOL; i++ {
if !flCfg.NValue.Contains(identIndex) {
tracef("parseConfiguredFlag(...) identIndex=%d exceeds expected=%v; breaking", identIndex, flCfg.NValue)
break
}
p.next()
switch p.tok {
case ARG_DELIMITER:
nodes = append(nodes, &ArgDelimiter{})
continue
case ASSIGN:
nodes = append(nodes, &Assign{})
continue
case IDENT, STDIN_FLAG:
name := fmt.Sprintf("%d", identIndex)
tracef("parseConfiguredFlag(...) checking for name of identIndex=%d", identIndex)
if len(flCfg.ValueNames) > identIndex {
name = flCfg.ValueNames[identIndex]
tracef("parseConfiguredFlag(...) setting name=%s from config value names", name)
} else if len(flCfg.ValueNames) == 1 && (flCfg.NValue == OneOrMoreValue || flCfg.NValue == ZeroOrMoreValue) {
name = fmt.Sprintf("%s.%d", flCfg.ValueNames[0], identIndex)
tracef("parseConfiguredFlag(...) setting name=%s from repeating value name", name)
} else {
tracef("parseConfiguredFlag(...) setting name=%s", name)
}
values[name] = p.lit
if p.tok == STDIN_FLAG {
nodes = append(nodes, &StdinFlag{})
} else {
nodes = append(nodes, &Ident{Literal: p.lit})
}
identIndex++
default:
tracef("parseConfiguredFlag(...) breaking on %s %q %v; setting buffered=true", p.tok, p.lit, p.pos)
p.buffered = true
if len(nodes) > 0 {
node.Nodes = nodes
}
if len(values) > 0 {
node.Values = values
}
return node
}
}
if len(nodes) > 0 {
node.Nodes = nodes
}
if len(values) > 0 {
node.Values = values
}
return node
}
func (p *parser) parsePassthrough() Node {
nodes := []Node{}
for ; p.tok != EOL; p.next() {
nodes = append(nodes, &Ident{Literal: p.lit})
}
if len(nodes) == 0 {
return nil
}
return &PassthroughArgs{Nodes: nodes}
}

View File

@@ -1,146 +0,0 @@
package argh
const (
OneOrMoreValue NValue = -2
ZeroOrMoreValue NValue = -1
ZeroValue NValue = 0
)
var (
POSIXyParserConfig = NewParserConfig(
nil,
POSIXyScannerConfig,
)
)
type NValue int
func (nv NValue) Contains(i int) bool {
tracef("NValue.Contains(%v)", i)
if i < int(ZeroValue) {
return false
}
if nv == OneOrMoreValue || nv == ZeroOrMoreValue {
return true
}
return int(nv) > i
}
type ParserConfig struct {
Prog CommandConfig
ScannerConfig *ScannerConfig
}
func NewParserConfig(prog *CommandConfig, sCfg *ScannerConfig) *ParserConfig {
if sCfg == nil {
sCfg = POSIXyScannerConfig
}
if prog == nil {
prog = &CommandConfig{}
}
prog.init()
pCfg := &ParserConfig{
Prog: *prog,
ScannerConfig: sCfg,
}
return pCfg
}
type CommandConfig struct {
NValue NValue
ValueNames []string
Flags *Flags
Commands *Commands
}
func (cCfg *CommandConfig) init() {
if cCfg.ValueNames == nil {
cCfg.ValueNames = []string{}
}
if cCfg.Flags == nil {
cCfg.Flags = &Flags{}
}
if cCfg.Commands == nil {
cCfg.Commands = &Commands{}
}
}
func (cCfg *CommandConfig) GetCommandConfig(name string) (CommandConfig, bool) {
tracef("CommandConfig.GetCommandConfig(%q)", name)
if cCfg.Commands == nil {
cCfg.Commands = &Commands{Map: map[string]CommandConfig{}}
}
return cCfg.Commands.Get(name)
}
func (cCfg *CommandConfig) GetFlagConfig(name string) (FlagConfig, bool) {
tracef("CommandConfig.GetFlagConfig(%q)", name)
if cCfg.Flags == nil {
cCfg.Flags = &Flags{Map: map[string]FlagConfig{}}
}
return cCfg.Flags.Get(name)
}
type FlagConfig struct {
NValue NValue
Persist bool
ValueNames []string
}
type Flags struct {
Parent *Flags
Map map[string]FlagConfig
Automatic bool
}
func (fl *Flags) Get(name string) (FlagConfig, bool) {
tracef("Flags.Get(%q)", name)
if fl.Map == nil {
fl.Map = map[string]FlagConfig{}
}
flCfg, ok := fl.Map[name]
if !ok {
if fl.Automatic {
return FlagConfig{}, true
}
if fl.Parent != nil {
flCfg, ok = fl.Parent.Get(name)
return flCfg, ok && flCfg.Persist
}
}
return flCfg, ok
}
type Commands struct {
Map map[string]CommandConfig
}
func (cmd *Commands) Get(name string) (CommandConfig, bool) {
tracef("Commands.Get(%q)", name)
if cmd.Map == nil {
cmd.Map = map[string]CommandConfig{}
}
cmdCfg, ok := cmd.Map[name]
return cmdCfg, ok
}

View File

@@ -1,88 +0,0 @@
package argh
import (
"fmt"
"io"
"sort"
)
// ParserError is largely borrowed from go/scanner.Error
type ParserError struct {
Pos Position
Msg string
}
func (e ParserError) Error() string {
if e.Pos.IsValid() {
return e.Pos.String() + ":" + e.Msg
}
return e.Msg
}
// ParserErrorList is largely borrowed from go/scanner.ErrorList
type ParserErrorList []*ParserError
func (el *ParserErrorList) Add(pos Position, msg string) {
*el = append(*el, &ParserError{Pos: pos, Msg: msg})
}
func (el *ParserErrorList) Reset() { *el = (*el)[0:0] }
func (el ParserErrorList) Len() int { return len(el) }
func (el ParserErrorList) Swap(i, j int) { el[i], el[j] = el[j], el[i] }
func (el ParserErrorList) Less(i, j int) bool {
e := &el[i].Pos
f := &el[j].Pos
if e.Column != f.Column {
return e.Column < f.Column
}
return el[i].Msg < el[j].Msg
}
func (el ParserErrorList) Sort() {
sort.Sort(el)
}
func (el ParserErrorList) Error() string {
switch len(el) {
case 0:
return "no errors"
case 1:
return el[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", el[0], len(el)-1)
}
func (el ParserErrorList) Err() error {
if len(el) == 0 {
return nil
}
return el
}
func (el ParserErrorList) Is(other error) bool {
if _, ok := other.(ParserErrorList); ok {
return el.Error() == other.Error()
}
if v, ok := other.(*ParserErrorList); ok {
return el.Error() == (*v).Error()
}
return false
}
func PrintParserError(w io.Writer, err error) {
if list, ok := err.(ParserErrorList); ok {
for _, e := range list {
fmt.Fprintf(w, "%s\n", e)
}
} else if err != nil {
fmt.Fprintf(w, "%s\n", err)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,94 +0,0 @@
package argh
type Querier interface {
Program() (*Command, bool)
AST() []Node
}
func NewQuerier(nodes []Node) Querier {
return &defaultQuerier{nodes: nodes}
}
type defaultQuerier struct {
nodes []Node
}
func (dq *defaultQuerier) Program() (*Command, bool) {
if len(dq.nodes) == 0 {
tracef("Program nodes are empty")
return nil, false
}
tracef("Program node[0] is %T", dq.nodes[0])
v, ok := dq.nodes[0].(*Command)
if ok && v.Name == "" {
return v, false
}
return v, ok
}
func (dq *defaultQuerier) AST() []Node {
ret := []Node{}
for i, node := range dq.nodes {
tracef("AST i=%d node type=%T", i, node)
if _, ok := node.(*ArgDelimiter); ok {
continue
}
if _, ok := node.(*StopFlag); ok {
continue
}
if v, ok := node.(*CompoundShortFlag); ok {
if v.Nodes != nil {
ret = append(ret, NewQuerier(v.Nodes).AST()...)
}
continue
}
if v, ok := node.(*Command); ok {
astNodes := NewQuerier(v.Nodes).AST()
if len(astNodes) == 0 {
astNodes = nil
}
ret = append(
ret,
&Command{
Name: v.Name,
Values: v.Values,
Nodes: astNodes,
})
continue
}
if v, ok := node.(*Flag); ok {
astNodes := NewQuerier(v.Nodes).AST()
if len(astNodes) == 0 {
astNodes = nil
}
ret = append(
ret,
&Flag{
Name: v.Name,
Values: v.Values,
Nodes: astNodes,
})
continue
}
ret = append(ret, node)
}
return ret
}

View File

@@ -1,60 +0,0 @@
package argh_test
import (
"testing"
"git.meatballhat.com/x/box-o-sand/argh"
"github.com/stretchr/testify/require"
)
func TestQuerier_Program(t *testing.T) {
for _, tc := range []struct {
name string
args []string
cfg *argh.ParserConfig
exp string
expOK bool
}{
{
name: "typical",
args: []string{"pizzas", "ahoy", "--treatsa", "fun"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: &argh.Commands{
Map: map[string]argh.CommandConfig{
"ahoy": argh.CommandConfig{
Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"treatsa": argh.FlagConfig{NValue: 1},
},
},
},
},
},
},
},
exp: "pizzas",
expOK: true,
},
{
name: "minimal",
args: []string{"pizzas"},
exp: "pizzas",
expOK: true,
},
{
name: "invalid",
args: []string{},
expOK: false,
},
} {
t.Run(tc.name, func(ct *testing.T) {
pt, err := argh.ParseArgs(tc.args, tc.cfg)
require.Nil(ct, err)
prog, ok := argh.NewQuerier(pt.Nodes).Program()
require.Equal(ct, tc.expOK, ok)
require.Equal(ct, tc.exp, prog.Name)
})
}
}

View File

@@ -1,159 +0,0 @@
package argh
import (
"bufio"
"bytes"
"errors"
"io"
"log"
"unicode"
)
type Scanner struct {
r *bufio.Reader
i int
cfg *ScannerConfig
}
func NewScanner(r io.Reader, cfg *ScannerConfig) *Scanner {
if cfg == nil {
cfg = POSIXyScannerConfig
}
return &Scanner{
r: bufio.NewReader(r),
cfg: cfg,
}
}
func (s *Scanner) Scan() (Token, string, Pos) {
ch, pos := s.read()
if s.cfg.IsBlankspace(ch) {
_ = s.unread()
return s.scanBlankspace()
}
if s.cfg.IsAssignmentOperator(ch) {
return ASSIGN, string(ch), pos
}
if s.cfg.IsMultiValueDelim(ch) {
return MULTI_VALUE_DELIMITER, string(ch), pos
}
if ch == eol {
return EOL, "", pos
}
if ch == nul {
return ARG_DELIMITER, string(ch), pos
}
if unicode.IsGraphic(ch) {
_ = s.unread()
return s.scanArg()
}
return ILLEGAL, string(ch), pos
}
func (s *Scanner) read() (rune, Pos) {
ch, _, err := s.r.ReadRune()
s.i++
if errors.Is(err, io.EOF) {
return eol, Pos(s.i)
} else if err != nil {
log.Printf("unknown scanner error=%+v", err)
return eol, Pos(s.i)
}
return ch, Pos(s.i)
}
func (s *Scanner) unread() Pos {
_ = s.r.UnreadRune()
s.i--
return Pos(s.i)
}
func (s *Scanner) scanBlankspace() (Token, string, Pos) {
buf := &bytes.Buffer{}
ch, pos := s.read()
buf.WriteRune(ch)
for {
ch, pos = s.read()
if ch == eol {
break
} else if !s.cfg.IsBlankspace(ch) {
pos = s.unread()
break
} else {
_, _ = buf.WriteRune(ch)
}
}
return BS, buf.String(), pos
}
func (s *Scanner) scanArg() (Token, string, Pos) {
buf := &bytes.Buffer{}
ch, pos := s.read()
buf.WriteRune(ch)
for {
ch, pos = s.read()
if ch == eol || ch == nul || s.cfg.IsAssignmentOperator(ch) || s.cfg.IsMultiValueDelim(ch) {
pos = s.unread()
break
}
_, _ = buf.WriteRune(ch)
}
str := buf.String()
if len(str) == 0 {
return EMPTY, str, pos
}
ch0 := rune(str[0])
if len(str) == 1 {
if s.cfg.IsFlagPrefix(ch0) {
return STDIN_FLAG, str, pos
}
if s.cfg.IsAssignmentOperator(ch0) {
return ASSIGN, str, pos
}
return IDENT, str, pos
}
ch1 := rune(str[1])
if len(str) == 2 {
if s.cfg.IsFlagPrefix(ch0) && s.cfg.IsFlagPrefix(ch1) {
return STOP_FLAG, str, pos
}
if s.cfg.IsFlagPrefix(ch0) {
return SHORT_FLAG, str, pos
}
}
if s.cfg.IsFlagPrefix(ch0) {
if s.cfg.IsFlagPrefix(ch1) {
return LONG_FLAG, str, pos
}
return COMPOUND_SHORT_FLAG, str, pos
}
return IDENT, str, pos
}

View File

@@ -1,39 +0,0 @@
package argh
var (
// POSIXyScannerConfig defines a scanner config that uses '-'
// as the flag prefix, which also means that "--" is the "long
// flag" prefix, a bare "--" is considered STOP_FLAG, and a
// bare "-" is considered STDIN_FLAG.
POSIXyScannerConfig = &ScannerConfig{
AssignmentOperator: '=',
FlagPrefix: '-',
MultiValueDelim: ',',
}
)
type ScannerConfig struct {
AssignmentOperator rune
FlagPrefix rune
MultiValueDelim rune
}
func (cfg *ScannerConfig) IsFlagPrefix(ch rune) bool {
return ch == cfg.FlagPrefix
}
func (cfg *ScannerConfig) IsMultiValueDelim(ch rune) bool {
return ch == cfg.MultiValueDelim
}
func (cfg *ScannerConfig) IsAssignmentOperator(ch rune) bool {
return ch == cfg.AssignmentOperator
}
func (cfg *ScannerConfig) IsBlankspace(ch rune) bool {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
}
func (cfg *ScannerConfig) IsUnderscore(ch rune) bool {
return ch == '_'
}

View File

@@ -1,53 +0,0 @@
//go:generate stringer -type Token
package argh
import "fmt"
const (
ILLEGAL Token = iota
EOL
EMPTY // ''
BS // ' ' '\t' '\n'
IDENT // char group without flag prefix: 'some' 'words'
ARG_DELIMITER // rune(0)
ASSIGN // '='
MULTI_VALUE_DELIMITER // ','
LONG_FLAG // char group with double flag prefix: '--flag'
SHORT_FLAG // single char with single flag prefix: '-f'
COMPOUND_SHORT_FLAG // char group with single flag prefix: '-flag'
STDIN_FLAG // '-'
STOP_FLAG // '--'
nul = rune(0)
eol = rune(-1)
)
type Token int
// Position is adapted from go/token.Position
type Position struct {
Column int
}
func (p *Position) IsValid() bool { return p.Column > 0 }
func (p Position) String() string {
s := ""
if p.IsValid() {
s = fmt.Sprintf("%d", p.Column)
}
if s == "" {
s = "-"
}
return s
}
// Pos is borrowed from go/token.Pos
type Pos int
const NoPos Pos = 0
func (p Pos) IsValid() bool {
return p != NoPos
}

View File

@@ -1,35 +0,0 @@
// Code generated by "stringer -type Token"; DO NOT EDIT.
package argh
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ILLEGAL-0]
_ = x[EOL-1]
_ = x[EMPTY-2]
_ = x[BS-3]
_ = x[IDENT-4]
_ = x[ARG_DELIMITER-5]
_ = x[ASSIGN-6]
_ = x[MULTI_VALUE_DELIMITER-7]
_ = x[LONG_FLAG-8]
_ = x[SHORT_FLAG-9]
_ = x[COMPOUND_SHORT_FLAG-10]
_ = x[STDIN_FLAG-11]
_ = x[STOP_FLAG-12]
}
const _Token_name = "ILLEGALEOLEMPTYBSIDENTARG_DELIMITERASSIGNMULTI_VALUE_DELIMITERLONG_FLAGSHORT_FLAGCOMPOUND_SHORT_FLAGSTDIN_FLAGSTOP_FLAG"
var _Token_index = [...]uint8{0, 7, 10, 15, 17, 22, 35, 41, 62, 71, 81, 100, 110, 119}
func (i Token) String() string {
if i < 0 || i >= Token(len(_Token_index)-1) {
return "Token(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Token_name[_Token_index[i]:_Token_index[i+1]]
}

1
lcthw/.envrc Normal file
View File

@@ -0,0 +1 @@
export CFLAGS='-Werror -g'

1
lcthw/.gdbinit Normal file
View File

@@ -0,0 +1 @@
set debuginfod enabled off

8
lcthw/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
ex1
ex3
ex7
ex8
ex9
ex10
ex11
ex12

30
lcthw/Makefile Normal file
View File

@@ -0,0 +1,30 @@
CFLAGS ?= -Wall -g
GDBRUN = gdb --batch --ex run --ex bt --ex q --args
BUILD_TARGETS = ex1 ex3 ex7 ex8 ex9 ex10 ex11 ex12
TEST_TARGETS = ex1 ex3 ex7 ex8 ex9 ex11 ex12
.PHONY: all
all: build test
.PHONY: clean
clean:
rm -f $(BUILD_TARGETS)
.PHONY: build
build: $(BUILD_TARGETS)
.PHONY: gtest
gtest:
@$(foreach bt, $(TEST_TARGETS), make .gtest.$(bt) &&) \
printf '\ngYAY\n'
.gtest.%:
printf '\n==> %s\n' "$*" && $(GDBRUN) ./$*
.PHONY: test
test:
@$(foreach bt, $(TEST_TARGETS), make .test.$(bt) &&) \
printf '\nYAY\n'
.test.%:
printf '\n==> %s\n' "$*" && ./$*

12
lcthw/ex1.c Normal file
View File

@@ -0,0 +1,12 @@
#include <stdio.h>
/* This is a comment. */
int main(int argc, char *argv[])
{
int distance = 100;
// this is also a comment
printf("You are %d miles away.\n", distance);
return 0;
}

62
lcthw/ex10.c Normal file
View File

@@ -0,0 +1,62 @@
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc < 2) {
printf("ERROR: You need at least one argument.\n");
// this is how you abort a program
return 1;
}
int i = 0;
int arg = 1;
for (arg = 1; arg < argc; arg++) {
for (i = 0; argv[arg][i] != '\0'; i++) {
printf("(%d) ", arg);
char letter = argv[arg][i];
// lowercase letters
if (letter >= 'A' && letter <= 'Z') {
letter += ('a' - 'A');
}
switch (letter) {
case 'a':
printf("%d: 'a'\n", i);
break;
case 'e':
printf("%d: 'e'\n", i);
break;
case 'i':
printf("%d: 'i'\n", i);
break;
case 'o':
printf("%d: 'o'\n", i);
break;
case 'u':
printf("%d: 'u'\n", i);
break;
case 'y':
// why i > 2? is this a bug?
if (i > 2) {
// it's only sometimes Y
printf("%d: 'y'\n", i);
} else {
printf("%d: %c is not a vowel\n", i, letter);
}
break;
default:
printf("%d: %c is not a vowel\n", i, letter);
}
}
}
return 0;
}

48
lcthw/ex11.c Normal file
View File

@@ -0,0 +1,48 @@
#include <stdio.h>
int main(int argc, char *argv[])
{
int numbers[4] = { 0 };
char name[4] = { 'a' };
// first, print them out raw
printf("numbers: %d %d %d %d\n",
numbers[0], numbers[1], numbers[2], numbers[3]);
printf("name each: %c %c %c %c\n",
name[0], name[1], name[2], name[3]);
printf("name: %s\n", name);
// setup the numbers
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
// setup the name
name[0] = 'Z';
name[1] = 'e';
name[2] = 'd';
name[3] = '\0';
// then print them out initialized
printf("numbers: %d %d %d %d\n",
numbers[0], numbers[1], numbers[2], numbers[3]);
printf("name each: %c %c %c %c\n",
name[0], name[1], name[2], name[3]);
// print the name like a string
printf("name: %s\n", name);
// another way to use name
char *another = "Zed";
printf("another: %s\n", another);
printf("another each: %c %c %c %c\n",
another[0], another[1], another[2], another[3]);
return 0;
}

32
lcthw/ex12.c Normal file
View File

@@ -0,0 +1,32 @@
#include <stdio.h>
int main(int argc, char *argv[])
{
int areas[] = { 10, 12, 13, 14, 20 };
char name[] = "Zed";
char full_name[] = {
'Z', 'e', 'd',
' ', 'A', '.', ' ',
'S', 'h', 'a', 'w', '\0',
};
// WARNING: On some systems you may have to change the
// %ld in this code to a %u since it will use unsigned ints
printf("The size of an int: %ld\n", sizeof(int));
printf("The size of areas (int[]): %ld\n", sizeof(areas));
printf("The number of ints in areas: %ld\n",
sizeof(areas) / sizeof(int));
printf("The first area is %d, the 2nd %d.\n", areas[0], areas[1]);
printf("The size of a char: %ld\n", sizeof(char));
printf("The size of name (char[]): %ld\n", sizeof(name));
printf("The number of chars: %ld\n", sizeof(name) / sizeof(char));
printf("The size of full_name (char[]): %ld\n", sizeof(full_name));
printf("The number of chars: %ld\n",
sizeof(full_name) / sizeof(char));
printf("name=\"%s\" and full_name=\"%s\"\n", name, full_name);
return 0;
}

12
lcthw/ex3.c Normal file
View File

@@ -0,0 +1,12 @@
#include <stdio.h>
int main(int argc, char *argv[])
{
int age = 10;
int height = 72;
printf("I am %d years old.\n", age);
printf("I am %d inches tall.\n", height);
return 0;
}

43
lcthw/ex7.c Normal file
View File

@@ -0,0 +1,43 @@
#include <stdio.h>
int main(int argc, char* argv[])
{
int distance = 100;
float power = 2.345f;
double super_power = 56789.4532;
char initial = 'A';
char first_name[] = "Zed";
char last_name[] = "Shaw";
printf("You are %d miles away.\n", distance);
printf("You have %f levels of power.\n", power);
printf("You have %f awesome super powers.\n", super_power);
printf("I have an initial %c.\n", initial);
printf("I have a first name %s.\n", first_name);
printf("I have a last name %s.\n", last_name);
printf("My whole name is %s %c. %s.\n",
first_name, initial, last_name);
int bugs = 100;
double bug_rate = 1.2;
printf("You have %d bugs at the imaginary rate of %f.\n",
bugs, bug_rate);
long universe_of_defects = 1L * 1024L * 1024L * 1024L;
printf("The entire universe has %ld bugs.\n", universe_of_defects);
double expected_bugs = bugs * bug_rate;
printf("You are expected to have %f bugs.\n", expected_bugs);
double part_of_universe = expected_bugs / universe_of_defects;
printf("That is only a %e portion of the universe.\n",
part_of_universe);
// this makes no sense, just a demo of something weird
char nul_byte = '\0';
int care_percentage = bugs * nul_byte;
printf("Which means you should care %d%%.\n", care_percentage);
return 0;
}

21
lcthw/ex8.c Normal file
View File

@@ -0,0 +1,21 @@
#include <stdio.h>
int main(int argc, char* argv[])
{
int i = 0;
if (argc == 1) {
printf("You have no argument.\n");
} else if (argc > 1 && argc < 4) {
printf("Here's your arguments:\n");
for (i = 0; i < argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
} else {
printf("You have too many arguments.\n");
}
return 0;
}

15
lcthw/ex9.c Normal file
View File

@@ -0,0 +1,15 @@
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = 25;
while (i > 0) {
printf("%d", i);
i--;
}
// need this to add a final newline
printf("\n");
return 0;
}

1
leetcode/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.envrc

1
leetcode/.python-version Normal file
View File

@@ -0,0 +1 @@
3.12.0

File diff suppressed because one or more lines are too long

147
leetcode/pyproject.toml Normal file
View File

@@ -0,0 +1,147 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "leetcode"
description = 'What A Mess'
readme = "README.md"
version = "0.1.0"
requires-python = ">=3.7"
license = "MIT"
keywords = []
authors = [
{ name = "Dan Buch", email = "dan@meatballhat.com" },
]
classifiers = []
dependencies = [
"ipython",
"ipdb",
"matplotlib",
"numpy"
]
[tool.hatch.envs.default]
dependencies = [
"coverage[toml]>=6.5",
"pytest",
]
[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
cov-report = [
"- coverage combine",
"coverage report",
]
cov = [
"test-cov",
"cov-report",
]
[[tool.hatch.envs.all.matrix]]
python = ["3.7", "3.8", "3.9", "3.10", "3.11"]
[tool.hatch.envs.lint]
detached = true
dependencies = [
"black>=23.1.0",
"mypy>=1.0.0",
"ruff>=0.0.243",
]
[tool.hatch.envs.lint.scripts]
typing = "mypy --install-types --non-interactive {args:.}"
style = [
"ruff {args:.}",
"black --check --diff {args:.}",
]
fmt = [
"black {args:.}",
"ruff --fix {args:.}",
"style",
]
all = [
"style",
"typing",
]
[tool.black]
target-version = ["py37"]
line-length = 90
[tool.ruff]
target-version = "py37"
line-length = 90
select = [
"A",
"ARG",
"B",
"C",
"DTZ",
"E",
"EM",
"F",
"FBT",
"I",
"ICN",
"ISC",
"N",
"PLC",
"PLE",
"PLR",
"PLW",
"Q",
"RUF",
"S",
"T",
"TID",
"UP",
"W",
"YTT",
]
ignore = [
# Allow non-abstract empty methods in abstract base classes
"B027",
# Allow boolean positional values in function calls, like `dict.get(... True)`
"FBT003",
# Ignore checks for possible passwords
"S105", "S106", "S107",
# Ignore complexity
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
# Allow print func
"T201",
# Allow assert statements
"S101",
]
unfixable = [
# Don't touch unused imports
"F401",
]
[tool.ruff.isort]
known-first-party = ["leetcode"]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "all"
[tool.ruff.per-file-ignores]
# Tests can use magic values, assertions, and relative imports
"tests/**/*" = ["PLR2004", "S101", "TID252"]
[tool.coverage.run]
source_pkgs = ["leetcode", "tests"]
branch = true
parallel = true
omit = [
"src/leetcode/__about__.py",
]
[tool.coverage.paths]
leetcode = ["src/leetcode", "*/leetcode/src/leetcode"]
tests = ["tests", "*/leetcode/tests"]
[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]

57
leetcode/roman.py Normal file
View File

@@ -0,0 +1,57 @@
SIMPLE = {
"I": 1,
"V": 5,
"X": 10,
"L": 50,
"C": 100,
"D": 500,
"M": 1000,
}
SIMPLE_REVERSE = {v: k for k, v in SIMPLE.items()}
COMPOUND = {
"IV": 4,
"IX": 9,
"XL": 40,
"XC": 90,
"CD": 400,
"CM": 900,
}
PREFIXES = {k[0] for k in COMPOUND.keys()}
COMPOUND_REVERSE = {v: k for k, v in COMPOUND.items()}
ALL = SIMPLE | COMPOUND
ALL_REVERSE = {v: k for k, v in ALL.items()}
def i2r(i: int) -> str:
if i > 100_000:
raise ValueError(f"{i} is too silly")
r: list[str] = []
for int_val, roman_val in sorted(ALL_REVERSE.items(), reverse=True):
remainder = i % int_val
r += [roman_val] * int((i - remainder) / int_val)
i = remainder
return "".join(r)
def r2i(r: str) -> int:
total = 0
offset = 0
for i in range(len(r)):
if i + offset > len(r) - 1:
break
c = r[i + offset]
if c in PREFIXES and (i + offset + 1) < len(r) and c + r[i + offset + 1] in ALL:
total += ALL[c + r[i + offset + 1]]
offset += 1
continue
total += ALL[c]
return total

57
leetcode/spiral_matrix.py Normal file
View File

@@ -0,0 +1,57 @@
import itertools
import typing
def matrix_spiral(matrix: list[list[typing.Any]]) -> list[typing.Any]:
return [matrix[y][x] for x, y in matrix_spiral_path(matrix)]
def matrix_spiral_path(matrix: list[list[int]]) -> list[tuple[int, int]]:
snek = SpinSnek(matrix)
while snek.step():
...
return snek.path
COMPASS = (
(1, 0), # east
(0, 1), # south
(-1, 0), # west
(0, -1), # north
)
class SpinSnek:
def __init__(self, board: list[list[int]], loc: tuple[int, int] = (0, 0)):
self.max_loc: tuple[int, int] = (len(board[0]) - 1, len(board) - 1)
self.spinner: itertools.cycle[tuple[int, int]] = itertools.cycle(COMPASS)
self.direction = next(self.spinner)
self.path: list[tuple[int, int]] = [loc]
self.missteps: int = 0
def step(self) -> bool:
loc = self.path[-1]
next_loc: tuple[int, int] = (
loc[0] + self.direction[0],
loc[1] + self.direction[1],
)
if (
next_loc[0] > self.max_loc[0]
or next_loc[1] > self.max_loc[1]
or next_loc[0] < 0
or next_loc[1] < 0
or next_loc in self.path
):
self.direction = next(self.spinner)
if self.missteps > len(COMPASS) - 1:
return False
self.missteps += 1
return self.step()
self.missteps: int = 0
self.path.append(next_loc)
return True

169
leetcode/stdlib.py Normal file
View File

@@ -0,0 +1,169 @@
import dataclasses
import typing
class LinkedListNode(typing.Protocol):
val: int
next: typing.Optional["LinkedListNode"]
class ListNode:
"""ListNode is the leetcode "standard library" type used in linked lists"""
def __init__(self, val: int = 0, next: typing.Optional["ListNode"] = None): # no qa
self.val = val
self.next = next
class ListNodeRandom:
"""ListNodeRandom is another weirdo linked list thing from
leetcode that is obviously very different than a binary tree
node :upside_down_face:
"""
def __init__(
self,
x: int,
next: typing.Optional["ListNodeRandom"] = None,
random: typing.Optional["ListNodeRandom"] = None,
):
self.val = x
self.next = next
self.random = random
@dataclasses.dataclass
class ListNodeRandomNicely:
val: int
next: typing.Optional["ListNodeRandomNicely"] = None
random: typing.Optional["ListNodeRandomNicely"] = None
class BinaryTreeNode(typing.Protocol):
val: int
left: typing.Optional["BinaryTreeNode"]
right: typing.Optional["BinaryTreeNode"]
class TreeNode:
"""TreeNode is the leetcode "standard library" type used in binary trees"""
def __init__(
self,
val: int = 0,
left: typing.Optional["TreeNode"] = None,
right: typing.Optional["TreeNode"] = None,
):
self.val = val
self.left = left
self.right = right
@classmethod
def from_int(cls, val: int | None) -> typing.Optional["TreeNode"]:
if val is None:
return None
return TreeNode(val)
# __repr__ was added by me
def __repr__(self) -> str:
filtered_parts = []
for key, value in [
("val", self.val),
("left", self.left),
("right", self.right),
]:
if value is not None:
filtered_parts.append((key, value))
middle = ", ".join([f"{k}={v!r}" for k, v in filtered_parts])
return f"TreeNode({middle})"
# __eq__ was added by me
def __eq__(self, other: typing.Optional["TreeNode"]) -> bool:
return (
other is not None
and self.val == other.val
and self.left == other.left
and self.right == other.right
)
class ConnectableBinaryTreeNode(typing.Protocol):
val: int
left: typing.Optional["BinaryTreeNode"]
right: typing.Optional["BinaryTreeNode"]
next: typing.Optional["BinaryTreeNode"]
class Node:
"""Node is the *other* leetcode "standard library" type used in binary trees"""
def __init__(
self,
val: int = 0,
left: typing.Optional["Node"] = None,
right: typing.Optional["Node"] = None,
next: typing.Optional["Node"] = None, # no qa
):
self.val = val
self.left = left
self.right = right
self.next = next
# __repr__ was added by me
def __repr__(self) -> str:
filtered_parts = []
for key, value in [
("val", self.val),
("right", self.right),
("left", self.left),
("next", self.next),
]:
if value is not None:
filtered_parts.append((key, value))
middle = ", ".join([f"{k}={v!r}" for k, v in filtered_parts])
return f"Node({middle})"
# __eq__ was added by me
def __eq__(self, other: "Node") -> bool:
return (
other is not None
and self.val == other.val
and self.left == other.left
and self.right == other.right
and self.next == other.next
)
# __list__ was added by me
def __list__(self) -> list[int | None]:
ret = [self.val]
ret += self.right.__list__() if self.right is not None else [None]
ret += self.left.__list__() if self.left is not None else [None]
ret += self.next.__list__() if self.next is not None else [None]
return ret
class NeighborlyNode:
"""NeighborlyNode is a "Node" type used in leetcode graph puzzles"""
def __init__(self, val=0, neighbors=None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
class NeighborlyNodeNicely(typing.NamedTuple):
val: int
neighbors: list["NeighborlyNodeNicely"]
def __eq__(self, other: typing.Optional["NeighborlyNodeNicely"]) -> bool:
return (
other is not None
and self.val == other.val
and [n.val for n in self.neighbors] == [n.val for n in other.neighbors]
)

640
leetcode/stuff.py Normal file
View File

@@ -0,0 +1,640 @@
import collections.abc
import copy
import random
import typing
import stdlib
def yep(s: str) -> bool:
return s.strip().lower().startswith("y")
def guess_bisect_repl(lower: int, upper: int) -> int:
mid = lower + ((upper - lower) // 2)
if yep(input(f"is it {mid}? ")):
return mid
if yep(input(f"higher than {mid}? ")):
return guess_bisect_repl(mid, upper)
return guess_bisect_repl(lower, mid)
def find_sqrt_ish(n: int) -> int:
return int(find_bisect(0, n, gen_sqrt_check(n)))
def gen_sqrt_check(n: int) -> typing.Callable[[float], int]:
def check(mid: float) -> int:
mid_sq: float = mid * mid
if mid_sq == n:
return 0
if mid_sq < n:
return 1
return -1
return check
def find_bisect(
lower: float, upper: float, check: typing.Callable[[float], int]
) -> float:
mid: float = lower + ((upper - lower) / 2)
print(f"lower={lower} mid={mid} upper={upper}")
if mid == lower or mid == upper or check(mid) == 0:
return mid
if check(mid) == 1:
return find_bisect(mid, upper, check)
return find_bisect(lower, mid, check)
def cartesian_path(p0: tuple[int, int], p1: tuple[int, int]) -> list[tuple[int, int]]:
path: list[tuple[int, int]] = []
if p0 < p1:
for i in range(p0[1], p1[1]):
path.append((i, p0[0]))
for i in range(p0[0], p1[0]):
path.append((p1[1], i))
else:
for i in range(p0[1], p1[1] - 1, -1):
path.append((i, p0[0]))
for i in range(p0[0] - 1, p1[0], -1):
path.append((p1[1], i))
return path
def gen_matrix(width: int, height: int) -> list[list[int]]:
return [list(range(width)) for _ in range(height)]
class MinStack:
def __init__(self):
self._v: list[int] = []
self._min: list[int] = []
def push(self, val: int) -> None:
self._v.append(val)
self._min.append(min(val, self._min[-1] if self._min else val))
def pop(self) -> None:
self._v.pop(-1)
self._min.pop(-1)
def top(self) -> int:
return self._v[-1]
def getMin(self) -> int: # no qa
return self._min[-1]
def linked_list_to_list(head: stdlib.LinkedListNode | None) -> list[int]:
seen: set[int] = set()
ret: list[int] = []
while head is not None:
if hash(head) in seen:
return ret
seen.add(hash(head))
ret.append(head.val)
head = head.next
return ret
def sort_linked_list(head: stdlib.LinkedListNode | None) -> stdlib.LinkedListNode | None:
by_val: list[tuple[int, stdlib.LinkedListNode]] = []
ret: stdlib.LinkedListNode | None = None
while head is not None:
by_val.append((head.val, head))
head = head.next
cur = ret
for _, node in sorted(by_val, key=lambda v: v[0]):
if cur is None:
cur = ret = node
continue
cur.next = node
cur = cur.next
if cur is not None:
cur.next = None
return ret
def connect_binary_tree_right(
root: stdlib.ConnectableBinaryTreeNode | None,
) -> tuple[stdlib.ConnectableBinaryTreeNode | None, list[int | None]]:
if root is None:
return None, []
by_level = binary_tree_by_level(copy.deepcopy(root))
by_level = typing.cast(dict[int, list[stdlib.ConnectableBinaryTreeNode]], by_level)
serialized: list[int | None] = []
print("")
if 0 not in by_level or len(by_level[0]) == 0:
return None, []
connected_root = by_level[0][0]
for level, nodes in sorted(by_level.items(), key=lambda p: p[0]):
for i in range(len(nodes)):
serialized.append(nodes[i].val)
if len(nodes) > i + 1:
print(f"{'-' * level}> connecting {nodes[i].val} -> {nodes[i + 1].val}")
nodes[i].next = nodes[i + 1]
serialized.append(None)
return connected_root, serialized
def binary_tree_by_level(
root: stdlib.BinaryTreeNode,
) -> dict[int, list[stdlib.BinaryTreeNode]]:
combined: dict[int, list[stdlib.BinaryTreeNode]] = {}
for path in collect_binary_tree_levels(0, root):
level, node = path
combined.setdefault(level, [])
combined[level].insert(0, node)
return combined
def collect_binary_tree_levels(
level: int, node: stdlib.BinaryTreeNode | None
) -> typing.Iterator[tuple[int, stdlib.BinaryTreeNode]]:
if node is None:
return
yield (level, node)
yield from collect_binary_tree_levels(level + 1, node.right)
yield from collect_binary_tree_levels(level + 1, node.left)
def sum_binary_tree_path_ints(root: stdlib.BinaryTreeNode | None) -> int:
path_ints: list[int] = []
for path in collect_binary_tree_paths(root):
path_ints.append(int("".join([str(node.val) for node in path])))
return sum(path_ints)
def binary_tree_paths_as_lists(
paths: list[list[stdlib.BinaryTreeNode]],
) -> list[list[int]]:
paths_vals: list[list[int]] = []
for path in paths:
paths_vals.append([node.val for node in path])
return paths_vals
def collect_binary_tree_paths(
node: stdlib.BinaryTreeNode | None,
) -> typing.Iterator[list[stdlib.BinaryTreeNode]]:
if node is None:
return
if node.right is None and node.left is None:
yield [node]
return
if node.right is not None:
for path in collect_binary_tree_paths(node.right):
yield [node] + path
if node.left is not None:
for path in collect_binary_tree_paths(node.left):
yield [node] + path
def binary_tree_from_list(inlist: list[int | None]) -> stdlib.BinaryTreeNode | None:
if len(inlist) == 0:
return None
nodes: list[stdlib.BinaryTreeNode | None] = [
typing.cast(stdlib.BinaryTreeNode | None, stdlib.TreeNode.from_int(i))
for i in inlist
]
nodes_copy = nodes[::-1]
root = nodes_copy.pop()
for node in nodes:
if node is None:
continue
if len(nodes_copy) == 0:
break
node.left = nodes_copy.pop()
if len(nodes_copy) > 0:
node.right = nodes_copy.pop()
return root
def binary_tree_from_preorder_inorder(
preorder: list[int], inorder: list[int]
) -> stdlib.BinaryTreeNode | None:
preorder_reversed = preorder[::-1]
def subtree(left: list[int], right: list[int]) -> stdlib.BinaryTreeNode:
root: stdlib.BinaryTreeNode = typing.cast(
stdlib.BinaryTreeNode, stdlib.TreeNode(preorder_reversed.pop())
)
if len(left) > 1:
split_pos = left.index(preorder_reversed[-1])
root.left = subtree(left[:split_pos], left[split_pos + 1 :])
elif len(left) == 1:
preorder_reversed.remove(left[0])
root.left = typing.cast(stdlib.BinaryTreeNode, stdlib.TreeNode(left[0]))
if len(right) > 1:
split_pos = right.index(preorder_reversed[-1])
root.right = subtree(right[:split_pos], right[split_pos + 1 :])
elif len(right) == 1:
preorder_reversed.remove(right[0])
root.right = typing.cast(stdlib.BinaryTreeNode, stdlib.TreeNode(right[0]))
return root
split_pos = inorder.index(preorder[0])
return subtree(inorder[:split_pos], inorder[split_pos + 1 :])
class JumpSpace(typing.NamedTuple):
pos: int
val: int
moves: list["JumpSpace"]
@classmethod
def from_board(
cls, pos: int = 0, board: typing.Iterable[int] = ()
) -> typing.Optional["JumpSpace"]:
board = list(board)
if len(board) == 0:
return None
space = cls(pos, board[pos], [])
space.collect(board)
return space
def collect(self, board: list[int]) -> None:
del self.moves[:]
if self.pos > len(board) or len(board) == 0:
return
for n in range(self.pos + 1, self.pos + self.val + 1):
if n >= len(board):
break
self.moves.append(typing.cast(JumpSpace, JumpSpace.from_board(n, board)))
def jump_paths(self) -> list[list[int]]:
ret: list[list[int]] = [[self.pos]]
for next_space in self.moves:
for path in next_space.jump_paths():
ret.append([self.pos] + path)
return ret
def collect_complete_jump_paths_from_board(board: list[int]) -> list[list[int]]:
return [
p
for p in collect_jump_paths_from_board(board)
if len(p) > 0 and p[-1] >= len(board) - 1
]
def collect_jump_paths_from_board(board: list[int]) -> list[list[int]]:
space = JumpSpace.from_board(0, board)
if space is None:
return []
return space.jump_paths()
# NOTE: the expensive way goes like this
# complete_paths = collect_complete_jump_paths_from_board(board)
# if len(complete_paths) == 0:
# return -1
# return min([len(p) - 1 for p in complete_paths])
def count_min_jumps_from_board(board: list[int]) -> int:
return len(collect_min_jumps_from_board(board))
def collect_min_jumps_from_board(board: list[int]) -> list[int]:
if len(board) < 3:
return list(range(1, len(board)))
jumps: list[int] = []
range_begin: int = 0
val = board[range_begin]
range_end: int = range_begin + val + 1
while range_end < len(board):
potential_jumps = board[range_begin:range_end]
scored_jumps = [
(val + range_begin + i, val, range_begin + i)
for i, val in enumerate(potential_jumps)
]
_, val, space = max(scored_jumps)
jumps.append(space)
range_begin = space
range_end = range_begin + val + 1
return jumps + [len(board) - 1]
def h_index(citations: list[int]) -> int:
last_qualified = None
for i, citation_count in enumerate(list(sorted(citations, reverse=True))):
if citation_count >= i + 1:
last_qualified = i + 1
else:
break
return last_qualified or 0
class SlowRandomizedSet:
def __init__(self):
self._i: set[int] = set()
def insert(self, val: int) -> bool:
ok = val not in self._i
self._i.add(val)
return ok
def remove(self, val: int) -> bool:
if val in self._i:
self._i.remove(val)
return True
return False
def getRandom(self) -> int:
return random.choice(list(self._i))
class RandomizedSet:
def __init__(self):
self._l: list[int] = []
self._m: dict[int, int] = {}
def insert(self, val: int) -> bool:
if val in self._m:
return False
self._m[val] = len(self._l)
self._l.append(val)
return True
def remove(self, val: int) -> bool:
if val not in self._m:
return False
val_loc = self._m[val]
last_val = self._l[-1]
self._l[val_loc] = last_val
self._m[last_val] = val_loc
self._l.pop()
self._m.pop(val)
return True
def getRandom(self) -> int:
return random.choice(self._l)
class TrieNode(typing.NamedTuple):
value: str
kids: dict[str, "TrieNode"]
@property
def is_leaf(self) -> bool:
return "__self__" in self.kids
@classmethod
def leaf(cls) -> "TrieNode":
return cls("__self__", {})
class Trie:
def __init__(self):
self._root_node = TrieNode("", {})
def insert(self, word: str) -> None:
if len(word) == 0:
return
current_node = self._root_node
for prefix in [word[: i + 1] for i in range(len(word))]:
current_node.kids.setdefault(prefix, TrieNode(prefix, {}))
current_node = current_node.kids[prefix]
leaf = TrieNode.leaf()
current_node.kids[leaf.value] = leaf
def search(self, word: str) -> bool:
return self._has(word, prefix_ok=False)
def startsWith(self, prefix: str) -> bool:
return self._has(prefix, prefix_ok=True)
def _has(self, word: str, prefix_ok: bool) -> bool:
if len(word) == 0:
return True
reverse_path = [word[: i + 1] for i in range(len(word))][::-1]
current_node = self._root_node
while reverse_path and current_node is not None:
current_node = current_node.kids.get(reverse_path.pop())
return (
current_node is not None
and (current_node.is_leaf or prefix_ok)
and current_node.value == word
)
def count_factorial_trailing_zeroes(number: int) -> int:
divisor: int = 5
zeroes_count: int = 0
while divisor <= number:
zeroes_count += number // divisor
divisor *= 5
return zeroes_count
def copy_random_list(
head: stdlib.ListNodeRandom | None,
) -> stdlib.ListNodeRandom | None:
if head is None:
return None
ordered = []
cur = head
while cur is not None:
ordered.append(cur)
cur = cur.next
ordered_copy = [stdlib.ListNodeRandom(entry.val) for entry in ordered]
hash_idx = {hash(n): i for i, n in enumerate(ordered)}
for i, entry in enumerate(ordered):
if i + 1 < len(ordered_copy):
ordered_copy[i].next = ordered_copy[i + 1]
if entry.random is not None:
ordered_copy[i].random = ordered_copy[hash_idx[hash(entry.random)]]
return ordered_copy[0]
def sum_max_sub_array(nums: list[int]) -> int:
mmax = last = prev = nums[0]
for i in range(1, len(nums)):
prev = nums[i] + last
last = max(nums[i], prev)
mmax = max(mmax, last)
return mmax
def sum_max_sub_array_i(nums: list[int]) -> tuple[int, int]:
mmax_i: int = 0
mmax = last = prev = nums[0]
for i in range(1, len(nums)):
prev = nums[i] + last
last = max(nums[i], prev)
mmax_i = i if last > mmax else mmax_i
mmax = max(mmax, last)
return mmax_i, mmax
def sum_max_sub_array_accum(nums: list[int]) -> int:
accum: list[int] = [nums[0]]
for i in range(1, len(nums)):
prev: int = nums[i] + accum[-1]
accum.append(max(nums[i], prev))
return max(accum)
def accum_sub_array_maxes(nums: list[int]) -> list[int]:
accum: list[int] = [nums[0]]
for i in range(1, len(nums)):
prev: int = nums[i] + accum[-1]
accum.append(max(nums[i], prev))
return accum
def neighborly_node_from_list(inlist: list[list[int]]):
# Alias "Node" type for leetcode compat
Node = stdlib.NeighborlyNodeNicely
if len(inlist) == 0:
return None
outlist = [Node(i + 1, []) for i in range(len(inlist))]
for i in range(len(inlist)):
outlist[i].neighbors[:] = []
for neighbor_val in inlist[i]:
outlist[i].neighbors.append(outlist[neighbor_val - 1])
return outlist[0]
def neighborly_node_to_list(node) -> list[list[int]]:
serialized: dict[int, list[int]] = {}
for cur in traverse_neighborly_node(node, serialized):
if cur is None:
break
serialized[cur.val] = [n.val for n in cur.neighbors]
return [v for _, v in sorted(serialized.items())]
def traverse_neighborly_node(
node: stdlib.NeighborlyNodeNicely, memo: collections.abc.Container[int]
) -> typing.Iterator[stdlib.NeighborlyNodeNicely | None]:
yield node
if node is None:
return
for neighbor in node.neighbors:
if neighbor.val in memo:
continue
yield from traverse_neighborly_node(neighbor, memo)
def find_min_in_rotated_array(nums: list[int]) -> int:
if nums[0] <= nums[-1]:
return nums[0]
if len(nums) <= 3:
return min(nums)
if nums[len(nums) // 2] > nums[-1]:
return find_min_in_rotated_array(nums[len(nums) // 2 :])
return find_min_in_rotated_array(nums[: (len(nums) // 2) + 1])

15
leetcode/test_roman.py Normal file
View File

@@ -0,0 +1,15 @@
import pytest
import roman
@pytest.mark.parametrize(
("n", "expected"),
[
(3, "III"),
(58, "LVIII"),
(1994, "MCMXCIV"),
],
)
def test_int_to_roman(n: int, expected: str):
assert roman.i2r(n) == expected

View File

@@ -0,0 +1,46 @@
import pytest
import spiral_matrix
@pytest.mark.parametrize(
("matrix", "expected"),
[
(
[
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"],
],
["a", "b", "c", "f", "i", "h", "g", "d", "e"],
),
(
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
],
[1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7],
),
(
[
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
],
[] # noqa
+ [1, 2, 3, 4, 5, 6, 7, 8, 9] # right
+ [9, 9, 9] # down
+ [9, 8, 7, 6, 5, 4, 3, 2, 1] # left
+ [1, 1] # up
+ [1, 2, 3, 4, 5, 6, 7, 8] # right
+ [8] # down
+ [8, 7, 6, 5, 4, 3, 2] # left
+ [2, 3, 4, 5, 6, 7], # right
),
],
)
def test_matrix_spiral(matrix, expected):
assert spiral_matrix.matrix_spiral(matrix) == expected

505
leetcode/test_stuff.py Normal file
View File

@@ -0,0 +1,505 @@
import json
import pytest
import stuff
import stdlib
@pytest.mark.parametrize(
("n", "expected"),
[
(0, 0),
(1, 1),
(5, 2),
(4, 2),
(8, 2),
(9, 3),
],
)
def test_find_sqrt_ish(n: int, expected: int):
assert stuff.find_sqrt_ish(n) == expected
@pytest.mark.parametrize(
("ops", "expected"),
[
(
(
[
("new",),
("push", -2),
("push", 0),
("push", -3),
("getMin",),
("pop",),
("top",),
("getMin",),
]
),
[
None,
None,
None,
None,
-3,
None,
0,
-2,
],
)
],
)
def test_min_stack(ops: list[tuple[str] | tuple[str, int]], expected: list[int | None]):
returned: list[int | None] = []
inst: stuff.MinStack | None = None
for op in ops:
if len(op) == 1:
if op[0] == "new":
inst = stuff.MinStack()
returned.append(None)
continue
returned.append(getattr(inst, op[0])())
continue
method, arg = op
returned.append(getattr(inst, method)(arg))
assert returned == expected
@pytest.mark.parametrize(
("head", "expected"),
[
(None, None),
(
stdlib.ListNode(
4, stdlib.ListNode(2, stdlib.ListNode(1, stdlib.ListNode(3)))
),
stdlib.ListNode(
1, stdlib.ListNode(2, stdlib.ListNode(3, stdlib.ListNode(4)))
),
),
(
stdlib.ListNode(
4,
stdlib.ListNode(
19,
stdlib.ListNode(
14,
stdlib.ListNode(
5,
stdlib.ListNode(
-3,
stdlib.ListNode(
1,
stdlib.ListNode(
8,
stdlib.ListNode(
5, stdlib.ListNode(11, stdlib.ListNode(15))
),
),
),
),
),
),
),
),
stdlib.ListNode(
-3,
stdlib.ListNode(
1,
stdlib.ListNode(
4,
stdlib.ListNode(
5,
stdlib.ListNode(
5,
stdlib.ListNode(
8,
stdlib.ListNode(
11,
stdlib.ListNode(
14, stdlib.ListNode(15, stdlib.ListNode(19))
),
),
),
),
),
),
),
),
),
],
)
def test_sort_linked_list(
head: stdlib.LinkedListNode | None, expected: stdlib.LinkedListNode | None
):
if head is None:
assert stuff.sort_linked_list(head) == expected
return
assert stuff.linked_list_to_list(
stuff.sort_linked_list(head)
) == stuff.linked_list_to_list(expected)
@pytest.mark.parametrize(
("root", "expected"),
[
(
stdlib.Node(
1,
left=stdlib.Node(2, left=stdlib.Node(4), right=stdlib.Node(5)),
right=stdlib.Node(3, right=stdlib.Node(7)),
),
[1, None, 2, 3, None, 4, 5, 7, None],
),
],
)
def test_connect_binary_tree_right(
root: stdlib.ConnectableBinaryTreeNode | None, expected: list[int | None] | None
):
if expected is None:
assert root is None
return
connected, serialized = stuff.connect_binary_tree_right(root)
assert connected is not None
assert serialized == expected
@pytest.mark.parametrize(
("root", "expected"),
[
(
stdlib.Node(
4,
right=stdlib.Node(0),
left=stdlib.Node(9, right=stdlib.Node(1), left=stdlib.Node(5)),
),
1026,
),
],
)
def test_connect_binary_tree_sum_numbers(
root: stdlib.BinaryTreeNode | None, expected: int
):
assert stuff.sum_binary_tree_path_ints(root) == expected
@pytest.mark.parametrize(
("inlist", "expected"),
[
(
[3, 5, 1, 6, 2, 0, 8, None, None, 7, 4],
stdlib.TreeNode(
3,
left=stdlib.TreeNode(
5,
left=stdlib.TreeNode(6),
right=stdlib.TreeNode(
2,
left=stdlib.TreeNode(7),
right=stdlib.TreeNode(4),
),
),
right=stdlib.TreeNode(
1,
left=stdlib.TreeNode(0),
right=stdlib.TreeNode(8),
),
),
),
],
)
def test_binary_tree_from_list(
inlist: list[int | None], expected: stdlib.BinaryTreeNode | None
):
assert stuff.binary_tree_from_list(inlist) == expected
@pytest.mark.parametrize(
("preorder", "inorder", "expected"),
[
(
[3, 9, 20, 15, 7],
[9, 3, 15, 20, 7],
stdlib.TreeNode(
3,
left=stdlib.TreeNode(9),
right=stdlib.TreeNode(
20, left=stdlib.TreeNode(15), right=stdlib.TreeNode(7)
),
),
),
(
[-1],
[-1],
stdlib.TreeNode(-1),
),
(
[1, 2],
[1, 2],
stdlib.TreeNode(1, right=stdlib.TreeNode(2)),
),
],
)
def test_binary_tree_from_preorder_inorder(
preorder: list[int], inorder: list[int], expected: stdlib.BinaryTreeNode
):
assert stuff.binary_tree_from_preorder_inorder(preorder, inorder) == expected
@pytest.mark.parametrize(
("board", "expected"),
[
(
[2, 3, 1, 1, 4],
[
[0, 1, 2, 3, 4],
[0, 1, 3, 4],
[0, 1, 4],
[0, 2, 3, 4],
],
),
],
)
def test_collect_complete_jump_paths_from_board(
board: list[int], expected: list[list[int]]
):
assert list(sorted(stuff.collect_complete_jump_paths_from_board(board))) == expected
@pytest.mark.parametrize(
("board", "expected"),
[
(
[2, 3, 1, 1, 4],
2,
),
(
[2, 3, 0, 1, 4],
2,
),
(
[1],
0,
),
(
[1, 2],
1,
),
(
[6, 2, 6, 1, 7, 9, 3, 5, 3, 7, 2, 8, 9, 4, 7, 7, 2, 2, 8, 4, 6, 6, 1, 3],
4,
),
(
[3, 4, 3, 2, 5, 4, 3],
3,
),
(
[3, 2, 1],
1,
),
(
[1, 2, 3],
2,
),
],
)
def test_count_min_jumps_from_board(board: list[int], expected: int):
assert stuff.count_min_jumps_from_board(board) == expected
@pytest.mark.parametrize(
("citations", "expected"),
[
(
[3, 0, 6, 1, 5],
3,
),
(
[1, 3, 1],
1,
),
(
[100],
1,
),
],
)
def test_h_index(citations: list[int], expected: int):
assert stuff.h_index(citations) == expected
@pytest.mark.parametrize(
("cls",),
[
(stuff.SlowRandomizedSet,),
(stuff.RandomizedSet,),
],
)
def test_randomized_set(cls: type[stuff.RandomizedSet] | type[stuff.SlowRandomizedSet]):
inst = cls()
assert inst.insert(1) is True
assert inst.remove(2) is False
assert inst.insert(2) is True
assert inst.getRandom() in (1, 2)
assert inst.remove(1) is True
assert inst.insert(2) is False
assert inst.getRandom() == 2
inst = cls()
assert inst.insert(1) is True
assert inst.insert(10) is True
assert inst.insert(20) is True
assert inst.insert(30) is True
seen: set[int] = set()
for _ in range(10_000):
seen.add(inst.getRandom())
assert seen == {1, 10, 20, 30}
# ["remove","remove","insert","getRandom","remove","insert"]
# [[0],[0],[0],[],[0],[0]]
inst = cls()
assert inst.remove(0) is False
assert inst.remove(0) is False
assert inst.insert(0) is True
assert inst.getRandom() == 0
assert inst.remove(0) is True
assert inst.insert(0) is True
# ["RandomizedSet","insert","insert","remove","insert","remove","getRandom"]
# [[],[0],[1],[0],[2],[1],[]]
inst = cls()
assert inst.insert(0) is True
assert inst.insert(1) is True
assert inst.remove(0) is True
assert inst.insert(2) is True
assert inst.remove(1) is True
assert inst.getRandom() == 2
def test_trie_single_letter():
trie = stuff.Trie()
assert trie.insert("a") is None
assert trie.search("a") is True
assert trie.startsWith("a") is True
def test_trie_prefix_leaf():
trie = stuff.Trie()
assert trie.insert("apple") is None
assert trie.search("apple") is True
assert trie.search("app") is False
assert trie.startsWith("app") is True
assert trie.insert("app") is None
assert trie.search("app") is True
def test_trie_two_letter():
trie = stuff.Trie()
assert trie.insert("ab") is None
assert trie.search("a") is False
assert trie.startsWith("a") is True
def test_trie_busy():
trie = stuff.Trie()
assert trie.insert("app") is None
assert trie.insert("apple") is None
assert trie.insert("beer") is None
assert trie.insert("add") is None
assert trie.insert("jam") is None
assert trie.insert("rental") is None
assert trie.search("apps") is False
assert trie.search("app") is True
assert trie.search("ad") is False
assert trie.search("applepie") is False
assert trie.search("rest") is False
assert trie.search("jan") is False
assert trie.search("rent") is False
assert trie.search("beer") is True
assert trie.search("jam") is True
assert trie.startsWith("apps") is False
assert trie.startsWith("app") is True
assert trie.startsWith("ad") is True
assert trie.startsWith("applepie") is False
assert trie.startsWith("rest") is False
assert trie.startsWith("jan") is False
assert trie.startsWith("rent") is True
assert trie.startsWith("beer") is True
assert trie.startsWith("jam") is True
@pytest.mark.parametrize(
("nums", "expected"),
[
(
[-2, 1, -3, 4, -1, 2, 1, -5, 4],
6,
),
(
[1],
1,
),
(
[5, 4, -1, 7, 8],
23,
),
(
[-2, 1],
1,
),
(
json.load(open(".testdata/max_sub_array0.json")),
11081,
),
],
)
def test_max_sub_array(nums: list[int], expected: int):
assert stuff.sum_max_sub_array(nums) == expected
@pytest.mark.parametrize(
("inlist",),
[
([[2, 3, 4], [1, 7], [1], [1, 5, 6, 8], [4], [4], [2], [4]],),
([[2, 4], [1, 3], [2, 4], [1, 3]],),
],
)
def test_copy_neighborly_node(inlist):
orig = stuff.neighborly_node_from_list(inlist)
copied = stuff.neighborly_node_from_list(stuff.neighborly_node_to_list(orig))
assert id(orig) != id(copied)
assert orig == copied
assert stuff.neighborly_node_to_list(orig) == stuff.neighborly_node_to_list(copied)
@pytest.mark.parametrize(
("nums", "expected"),
[
([3, 4, 5, 1, 2], 1),
([4, 5, 6, 7, 0, 1, 2], 0),
([11, 13, 15, 17], 11),
],
)
def test_find_min_in_rotated_array(nums: list[int], expected: int):
assert stuff.find_min_in_rotated_array(nums) == expected

1
modernc/01/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
getting-started

23
modernc/01/bad.c Normal file
View File

@@ -0,0 +1,23 @@
/* This may look like nonsense, but really is -*- mode: C -*- */
/* The main thing that this program does. */
void main() {
// Decralations
int i;
double A[5] = {
9.0,
2.9,
3.E+25,
.00007,
};
// Doing some work
for (i = 0; i < 5; ++i) {
printf("element %d is %g, \tits square is %g\n",
i,
A[i],
A[i]*A[i]);
}
return 0;
}

View File

@@ -0,0 +1,24 @@
/* This may look like nonsense, but really is -*- mode: C -*- */
#include <stdlib.h>
#include <stdio.h>
/* The main thing that this program does. */
int main(void) {
// Decralations
double A[5] = {
[0] = 9.0,
[1] = 2.9,
[4] = 3.E+25,
[3] = .00007,
};
// Doing some work
for (size_t i = 0; i < 5; ++i) {
printf("element %zu is %g, \tits square is %g\n",
i,
A[i],
A[i]*A[i]);
}
return EXIT_SUCCESS;
}

2
modernc/03/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
challenge-1
heron

90
modernc/03/array.c Normal file
View File

@@ -0,0 +1,90 @@
#include <stdlib.h>
#include "array.h"
void IntArray_new(IntArray* a, size_t size) {
a->arr = malloc(size * sizeof(int));
a->used = 0;
a->size = size;
}
int IntArray_append(IntArray* a, int el) {
if (a->used == a->size) {
a->size *= 2;
a->arr = realloc(a->arr, a->size * sizeof(int));
}
a->used++;
a->arr[a->used] = el;
return a->used;
}
int IntArray_get(IntArray* a, int idx, int dflt) {
if (idx > a->used) {
return dflt;
}
return a->arr[idx];
}
int IntArray_length(IntArray* a) {
return a->used;
}
void IntArray_mergesort(IntArray* a) {
IntArray left_side;
IntArray right_side;
size_t split = a->used / 2;
IntArray_new(&left_side, split);
IntArray_new(&right_side, split);
for (int i = 0; i < split; ++i) {
IntArray_append(&left_side, IntArray_get(a, i, 0));
}
for (int i = split; i < a->used; ++i) {
IntArray_append(&right_side, IntArray_get(a, i, 0));
}
IntArray_mergesort(&left_side);
IntArray_mergesort(&right_side);
IntArray_mergesort_merge(a, &left_side, &right_side);
}
void IntArray_mergesort_merge(IntArray* a, IntArray* left_side, IntArray* right_side) {
IntArray_free(a);
size_t i = 0;
size_t j = 0;
while (i < left_side->used && j < right_side->used) {
if (left_side->arr[i] <= right_side->arr[j]) {
IntArray_append(a, left_side->arr[i]);
i++;
} else {
IntArray_append(a, right_side->arr[j]);
j++;
}
}
for (size_t li = 0; li < left_side->used; ++li) {
IntArray_append(a, left_side->arr[li]);
}
for (size_t ri = 0; ri < right_side->used; ++ri) {
IntArray_append(a, right_side->arr[ri]);
}
}
void IntArray_quicksort(IntArray* a) {
return;
}
void IntArray_free(IntArray* a) {
free(a->arr);
a->arr = NULL;
a->used = a->size = 0;
}

22
modernc/03/array.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef INCLUDED_ARRAY_H
#include <stdlib.h>
typedef struct {
int *arr;
size_t used;
size_t size;
} IntArray;
void IntArray_new(IntArray*, size_t);
int IntArray_append(IntArray*, int);
int IntArray_get(IntArray*, int, int);
int IntArray_length(IntArray*);
void IntArray_mergesort(IntArray*);
void IntArray_mergesort_merge(IntArray*, IntArray*, IntArray*);
void IntArray_quicksort(IntArray*);
void IntArray_free(IntArray*);
#define INCLUDED_ARRAY_H 1
#endif

114
modernc/03/challenge-1.c Normal file
View File

@@ -0,0 +1,114 @@
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include "array.h"
#define IODASH "-"
void usage(char* prog) {
fprintf(stderr, "Usage: %s [-f|--input-file <input-file>] [-o|--output-file <output-file>]\n", prog);
}
int main(int argc, char **argv) {
char* input_file = IODASH;
char* output_file = IODASH;
int c = 0;
while (1) {
static struct option long_options[] = {
{"input-file", required_argument, 0, 'f'},
{"output-file", required_argument, 0, 'o'},
};
int option_index = 0;
c = getopt_long(argc, argv, "f:o:", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 'f':
input_file = optarg;
break;
case 'o':
output_file = optarg;
break;
default:
usage(argv[0]);
return 1;
}
}
if (strcmp(input_file, IODASH) == 0) {
fprintf(stderr, "(reading from stdin)\n");
}
if (strcmp(output_file, IODASH) == 0) {
fprintf(stderr, "(writing to stdout)\n");
}
printf("input_file='%s' output_file='%s'\n", input_file, output_file);
FILE* instream;
instream = stdin;
if (strcmp(input_file, IODASH) != 0) {
instream = fopen(input_file, "r");
}
if (instream == NULL) {
perror("opening input file");
return EXIT_FAILURE;
}
FILE* outstream;
outstream = stdout;
if (strcmp(output_file, IODASH) != 0) {
outstream = fopen(output_file, "r");
}
if (outstream == NULL) {
perror("opening output file");
return EXIT_FAILURE;
}
int line_int;
ssize_t nread;
char* endptr = NULL;
char* line = NULL;
size_t len = 0;
IntArray accum;
IntArray_new(&accum, 100);
while ((nread = getline(&line, &len, instream)) != -1) {
IntArray_append(&accum, atoi(line));
}
printf("Accumulated %i ints\n", IntArray_length(&accum));
IntArray_mergesort(&accum);
printf("Merge sorted:\n");
for (int i = 0; i < accum.used; ++i) {
printf("%i: %i\n", i, IntArray_get(&accum, i, 0));
}
if (strcmp(input_file, IODASH) != 0) {
fclose(instream);
}
if (strcmp(output_file, IODASH) != 0) {
fclose(outstream);
}
return EXIT_SUCCESS;
}

40
modernc/03/heron.c Normal file
View File

@@ -0,0 +1,40 @@
#include <stdlib.h>
#include <stdio.h>
/* lower and upper iteration limits centered around 1.0 */
static double const eps1m01 = 1.0 - 0x1P-01;
static double const eps1p01 = 1.0 + 0x1P-01;
static double const eps1m24 = 1.0 - 0x1P-24;
static double const eps1p24 = 1.0 + 0x1P-24;
int main(int argc, char* argv[argc+1]) {
for (int i = 1; i < argc; ++i) {
double const a = strtod(argv[i], 0);
double x = 1.0;
for (;;) {
double prod = a*x;
if (prod < eps1m01) {
x *= 2.0;
} else if (eps1p01 < prod) {
x *= 0.5;
} else {
break;
}
}
for (;;) {
double prod = a*x;
if ((prod < eps1m24) || (eps1p24 < prod)) {
x *= (2.0 - prod);
} else {
break;
}
}
printf("heron: a=%.5e,\tx=%.5e,\ta*x=%.12f\n",
a, x, a*x);
}
return EXIT_SUCCESS;
}

1
modernc/05/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
basic

9
modernc/05/basic.c Normal file
View File

@@ -0,0 +1,9 @@
#include <stdio.h>
int main() {
double x = 5.0;
double y = 3.0;
x = (x * 1.5) - y;
printf("x is \%g\n", x);
}

1
personal-home-page/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/vendor/

View File

@@ -0,0 +1,11 @@
<?php
require_once 'vendor/autoload.php';
$hello_cmd = new Commando\Command();
$hello_cmd->option()->require()->describedAs('A person\'s name');
$name = $hello_cmd[0];
echo "Hello, $name", PHP_EOL;

View File

@@ -0,0 +1,14 @@
{
"name": "meatballhat/personal-home-page",
"type": "library",
"require": {
"nategood/commando": "^0.4.0"
},
"license": "MIT",
"authors": [
{
"name": "Dan Buch",
"email": "dan@meatballhat.com"
}
]
}

124
personal-home-page/composer.lock generated Normal file
View File

@@ -0,0 +1,124 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "4aa26239cdb17b13a548c56ac84b5749",
"packages": [
{
"name": "kevinlebrun/colors.php",
"version": "0.4.1",
"source": {
"type": "git",
"url": "https://github.com/kevinlebrun/colors.php.git",
"reference": "d132f36d06e48ea080855af19b4bcb1fb615224a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kevinlebrun/colors.php/zipball/d132f36d06e48ea080855af19b4bcb1fb615224a",
"reference": "d132f36d06e48ea080855af19b4bcb1fb615224a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.*",
"satooshi/php-coveralls": "dev-master",
"squizlabs/php_codesniffer": "1.*"
},
"type": "library",
"autoload": {
"psr-0": {
"Colors": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kevin Le Brun",
"email": "lebrun.k@gmail.com",
"homepage": "http://kevinlebrun.fr",
"role": "developer"
}
],
"description": "Colors for PHP CLI scripts",
"homepage": "https://github.com/kevinlebrun/colors.php",
"keywords": [
"cli",
"color",
"colors",
"console",
"shell"
],
"support": {
"issues": "https://github.com/kevinlebrun/colors.php/issues",
"source": "https://github.com/kevinlebrun/colors.php/tree/0.4.1"
},
"time": "2014-12-23T01:23:37+00:00"
},
{
"name": "nategood/commando",
"version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/nategood/commando.git",
"reference": "8fedd49fcb694faf60d87d5c2c4defdffa298fa0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nategood/commando/zipball/8fedd49fcb694faf60d87d5c2c4defdffa298fa0",
"reference": "8fedd49fcb694faf60d87d5c2c4defdffa298fa0",
"shasum": ""
},
"require": {
"kevinlebrun/colors.php": "~0.2",
"php": ">=5.6"
},
"type": "library",
"autoload": {
"psr-0": {
"Commando": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nate Good",
"email": "me@nategood.com"
}
],
"description": "PHP CLI Commando Style",
"homepage": "http://github.com/nategood/commando",
"keywords": [
"automation",
"cli",
"command",
"command line",
"command line interface",
"scripting"
],
"support": {
"issues": "https://github.com/nategood/commando/issues",
"source": "https://github.com/nategood/commando/tree/v0.4.0"
},
"time": "2019-07-10T02:37:11+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<?php
echo "<h3>Hi, I'm a PHP script!</h3>";
?>
</body>
</html>

View File

@@ -0,0 +1,35 @@
<?php
$name = 'Flavio';
var_dump($name);
$age = 20;
var_dump($age);
$base = 20;
$height = 10;
$area = $base * $height;
$test = 'an example';
$example = "This is $test";
print $example . "\n";
$firstName = 'Flavio';
$lastName = 'Copes';
$fullName = $firstName . ' ' . $lastName;
print '$fullName = ' . $fullName . "\n";
print strlen($fullName) . "\n";
$weirdName = str_replace('avio', 'ower', $fullName);
print '$weirdName = ' . $weirdName . "\n";
print 'exploded $weirdName = ' . var_dump(explode(' ', $weirdName)) . "\n";

View File

@@ -0,0 +1 @@
<?php phpinfo(); ?>

View File

@@ -0,0 +1,6 @@
<pre>
<?php
echo $_SERVER['HTTP_USER_AGENT'], PHP_EOL;
var_dump($_SERVER);
?>
</pre>

1876
piston-tutorials/getting-started/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
[package]
name = "spinning-square"
version = "0.1.0"
authors = [
"Dan Buch <dan@meatballhat.com>",
"TyOverby <ty@pre-alpha.com>",
"Nikita Pekin <contact@nikitapek.in>"
]
[[bin]]
name = "spinning-square"
[dependencies]
piston = "0.53.0"
piston2d-graphics = "0.43.0"
pistoncore-glutin_window = "0.70.1"
piston2d-opengl_graphics = "0.82.0"

View File

@@ -0,0 +1,70 @@
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventSettings, Events};
use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent};
use piston::window::WindowSettings;
pub struct App {
gl: GlGraphics,
rotation: f64,
}
impl App {
fn render(&mut self, args: &RenderArgs) {
use graphics::*;
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
let square = rectangle::square(0.0, 0.0, 50.0);
let rotation = self.rotation;
let (x, y) = (args.window_size[0] / 2.0, args.window_size[1] / 2.0);
self.gl.draw(args.viewport(), |c, gl| {
clear(GREEN, gl);
let transform = c
.transform
.trans(x, y)
.rot_rad(rotation)
.trans(-25.0, -25.0);
rectangle(RED, square, transform, gl);
})
}
fn update(&mut self, args: &UpdateArgs) {
self.rotation += 2.0 * args.dt;
}
}
fn main() {
let opengl = OpenGL::V3_2;
let mut window: Window = WindowSettings::new("spinning-square", [200, 200])
.graphics_api(opengl)
.exit_on_esc(true)
.build()
.unwrap();
let mut app = App {
gl: GlGraphics::new(opengl),
rotation: 0.0,
};
let mut events = Events::new(EventSettings::new());
while let Some(e) = events.next(&mut window) {
if let Some(args) = e.render_args() {
app.render(&args);
}
if let Some(args) = e.update_args() {
app.update(&args);
}
}
}

1876
piston-tutorials/sudoku/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
[package]
name = "sudoku"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
piston = "0.53.2"
piston2d-graphics = "0.43.0"
piston2d-opengl_graphics = "0.82.0"
pistoncore-glutin_window = "0.70.1"

Binary file not shown.

View File

@@ -0,0 +1,99 @@
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Sans.
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Mono.
Copyright (c) 2014, Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,155 @@
//! Game board logic.
use std::fs::read_to_string;
const SIZE: usize = 9;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Cell {
pub value: u8,
pub loaded: bool,
pub invalid: bool,
}
#[derive(Debug, PartialEq)]
pub struct Gameboard {
pub cells: [[Cell; SIZE]; SIZE],
pub completed: bool,
}
impl Gameboard {
pub fn new() -> Gameboard {
Gameboard {
cells: [[Cell::default(); SIZE]; SIZE],
completed: false,
}
}
pub fn from_cells(cells: [[u8; SIZE]; SIZE]) -> Gameboard {
let mut ret = Gameboard::new();
for (i, row) in cells.iter().enumerate() {
for (j, &col) in row.iter().enumerate() {
ret.cells[i][j] = Cell {
value: col,
loaded: col != 0,
invalid: false,
};
}
}
ret
}
pub fn char(&self, ind: [usize; 2]) -> Option<char> {
Some(match self.cells[ind[1]][ind[0]].value {
1 => '1',
2 => '2',
3 => '3',
4 => '4',
5 => '5',
6 => '6',
7 => '7',
8 => '8',
9 => '9',
_ => return None,
})
}
pub fn set(&mut self, ind: [usize; 2], val: u8) {
if !self.cells[ind[1]][ind[0]].loaded {
self.validate(ind, val);
self.cells[ind[1]][ind[0]].value = val;
}
self.completed = self
.cells
.iter()
.flatten()
.all(|cell| !cell.invalid && cell.value != 0);
}
pub fn load_sdm(filename: &str) -> Self {
let data = read_to_string(filename).expect("failed to read SDM file");
let mut cells = [[Cell::default(); SIZE]; SIZE];
let mut row = 0;
let mut col = 0;
for c in data.chars() {
if col == SIZE {
col = 0;
row += 1;
}
if let Some(v) = c.to_digit(10) {
let value = v as u8;
cells[row][col] = Cell {
value,
loaded: value != 0,
invalid: false,
};
col += 1;
}
}
Self {
cells,
completed: false,
}
}
fn validate(&mut self, ind: [usize; 2], val: u8) {
let [b, a] = ind;
for i in 0..SIZE {
if i == a {
continue;
}
if self.cells[a][i].value == val {
self.cells[a][b].invalid = true;
return;
}
}
for i in 0..SIZE {
if i == b {
continue;
}
if self.cells[i][b].value == val {
self.cells[a][b].invalid = true;
return;
}
}
let (row, col) = (a / 3, b / 3);
for i in 3 * row..3 * row + 3 {
for j in 3 * col..3 * col + 3 {
if i == a && j == b {
continue;
}
if self.cells[i][j].value == val {
self.cells[a][b].invalid = true;
return;
}
}
}
self.cells[a][b].invalid = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_sdm() {
let got = Gameboard::load_sdm("static/puzzle.sdm");
let want = Gameboard::from_cells([
[0, 1, 6, 4, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 9, 0, 0, 0],
[4, 0, 0, 0, 0, 0, 0, 6, 2],
[0, 7, 0, 2, 3, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 3],
[0, 0, 3, 0, 8, 7, 0, 4, 0],
[9, 6, 0, 0, 0, 0, 0, 0, 5],
[0, 0, 0, 8, 0, 0, 0, 0, 7],
[0, 0, 0, 0, 0, 6, 8, 2, 0],
]);
assert_eq!(got, want);
}
}

View File

@@ -0,0 +1,57 @@
//! Gameboard controller
use piston::GenericEvent;
use crate::Gameboard;
pub struct GameboardController {
pub gameboard: Gameboard,
pub selected_cell: Option<[usize; 2]>,
cursor_pos: [f64; 2],
}
impl GameboardController {
pub fn new(gameboard: Gameboard) -> GameboardController {
GameboardController {
gameboard: gameboard,
selected_cell: None,
cursor_pos: [0.0; 2],
}
}
pub fn event<E: GenericEvent>(&mut self, pos: [f64; 2], size: f64, e: &E) {
use piston::input::{Button, Key, MouseButton};
if let Some(pos) = e.mouse_cursor_args() {
self.cursor_pos = pos;
}
if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
let x = self.cursor_pos[0] - pos[0];
let y = self.cursor_pos[1] - pos[1];
if x >= 0.0 && x < size && y >= 0.0 && y < size {
let cell_x = (x / size * 9.0) as usize;
let cell_y = (y / size * 9.0) as usize;
self.selected_cell = Some([cell_x, cell_y]);
}
}
if let Some(Button::Keyboard(key)) = e.press_args() {
if let Some(ind) = self.selected_cell {
match key {
Key::D1 => self.gameboard.set(ind, 1),
Key::D2 => self.gameboard.set(ind, 2),
Key::D3 => self.gameboard.set(ind, 3),
Key::D4 => self.gameboard.set(ind, 4),
Key::D5 => self.gameboard.set(ind, 5),
Key::D6 => self.gameboard.set(ind, 6),
Key::D7 => self.gameboard.set(ind, 7),
Key::D8 => self.gameboard.set(ind, 8),
Key::D9 => self.gameboard.set(ind, 9),
_ => {}
}
}
}
}
}

View File

@@ -0,0 +1,210 @@
//! Gameboard view.
use graphics::character::CharacterCache;
use graphics::types::Color;
use graphics::{Context, Graphics};
use crate::gameboard_controller::GameboardController;
pub struct GameboardViewSettings {
pub position: [f64; 2],
pub size: f64,
pub background_color: Color,
pub border_color: Color,
pub board_edge_color: Color,
pub section_edge_color: Color,
pub cell_edge_color: Color,
pub board_edge_radius: f64,
pub section_edge_radius: f64,
pub cell_edge_radius: f64,
pub selected_cell_background_color: Color,
pub text_color: Color,
pub loaded_cell_background_color: Color,
pub invalid_cell_background_color: Color,
pub invalid_selected_cell_background_color: Color,
pub completed_background_color: Color,
}
impl GameboardViewSettings {
pub fn new() -> GameboardViewSettings {
GameboardViewSettings {
position: [10.0; 2],
size: 400.0,
background_color: [0.8, 0.8, 1.0, 1.0],
border_color: [0.0, 0.0, 0.2, 1.0],
board_edge_color: [0.0, 0.0, 0.2, 1.0],
section_edge_color: [0.0, 0.0, 0.2, 1.0],
cell_edge_color: [0.0, 0.0, 0.2, 1.0],
board_edge_radius: 3.0,
section_edge_radius: 2.0,
cell_edge_radius: 1.0,
selected_cell_background_color: [0.9, 0.9, 1.0, 1.0],
text_color: [0.0, 0.0, 0.1, 1.0],
loaded_cell_background_color: [1.0, 1.0, 1.0, 1.0],
invalid_cell_background_color: [1.0, 0.0, 0.0, 1.0],
invalid_selected_cell_background_color: [1.0, 0.0, 0.5, 1.0],
completed_background_color: [0.0, 1.0, 0.0, 1.0],
}
}
}
pub struct GameboardView {
pub settings: GameboardViewSettings,
}
impl GameboardView {
pub fn new(settings: GameboardViewSettings) -> GameboardView {
GameboardView { settings: settings }
}
pub fn draw<G: Graphics, C>(
&self,
controller: &GameboardController,
glyphs: &mut C,
c: &Context,
g: &mut G,
) where
C: CharacterCache<Texture = G::Texture>,
{
use graphics::{Image, Line, Rectangle, Transformed};
let ref settings = self.settings;
let board_rect = [
settings.position[0],
settings.position[1],
settings.size,
settings.size,
];
if controller.gameboard.completed {
Rectangle::new(settings.completed_background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
} else {
Rectangle::new(settings.background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
for i in 0..9 {
for j in 0..9 {
if controller.gameboard.cells[i][j].loaded {
color_cell(
settings,
[j, i],
settings.loaded_cell_background_color,
c,
g,
);
} else if controller.gameboard.cells[i][j].invalid {
color_cell(
settings,
[j, i],
settings.invalid_cell_background_color,
c,
g,
);
}
}
}
if let Some(ind) = controller.selected_cell {
let cell = controller.gameboard.cells[ind[1]][ind[0]];
let color = if !cell.loaded {
if !cell.invalid {
settings.selected_cell_background_color
} else {
settings.invalid_selected_cell_background_color
}
} else {
settings.loaded_cell_background_color
};
color_cell(settings, ind, color, c, g);
}
}
let text_image = Image::new_color(settings.text_color);
let cell_size = settings.size / 9.0;
for j in 0..9 {
for i in 0..9 {
if let Some(ch) = controller.gameboard.char([i, j]) {
let pos = [
settings.position[0] + i as f64 * cell_size + 15.0,
settings.position[1] + j as f64 * cell_size + 34.0,
];
if let Ok(character) = glyphs.character(34, ch) {
let ch_x = pos[0] + character.left();
let ch_y = pos[1] - character.top();
let text_image = text_image.src_rect([
character.atlas_offset[0],
character.atlas_offset[1],
character.atlas_size[0],
character.atlas_size[1],
]);
text_image.draw(
character.texture,
&c.draw_state,
c.transform.trans(ch_x, ch_y),
g,
);
}
}
}
}
let cell_edge = Line::new(settings.cell_edge_color, settings.cell_edge_radius);
let section_edge = Line::new(settings.section_edge_color, settings.section_edge_radius);
for i in 0..9 {
let x = settings.position[0] + i as f64 / 9.0 * settings.size;
let y = settings.position[1] + i as f64 / 9.0 * settings.size;
let x2 = settings.position[0] + settings.size;
let y2 = settings.position[1] + settings.size;
let vline = [x, settings.position[1], x, y2];
let hline = [settings.position[0], y, x2, y];
if (i % 3) == 0 {
section_edge.draw(vline, &c.draw_state, c.transform, g);
section_edge.draw(hline, &c.draw_state, c.transform, g);
} else {
cell_edge.draw(vline, &c.draw_state, c.transform, g);
cell_edge.draw(hline, &c.draw_state, c.transform, g);
}
}
Rectangle::new_border(settings.board_edge_color, settings.board_edge_radius).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
}
}
fn color_cell<G: Graphics>(
settings: &GameboardViewSettings,
ind: [usize; 2],
color: [f32; 4],
c: &Context,
g: &mut G,
) {
use graphics::Rectangle;
let cell_size = settings.size / 9.0;
let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size];
let cell_rect = [
settings.position[0] + pos[0],
settings.position[1] + pos[1],
cell_size,
cell_size,
];
Rectangle::new(color).draw(cell_rect, &c.draw_state, c.transform, g);
}

View File

@@ -0,0 +1,57 @@
//////#![deny(missing_docs)]
//! An Sudoko please.
extern crate glutin_window;
use glutin_window::GlutinWindow;
use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
use piston::event_loop::{EventSettings, Events};
use piston::{EventLoop, RenderEvent, WindowSettings};
pub use crate::gameboard::Gameboard;
pub use crate::gameboard_controller::GameboardController;
pub use crate::gameboard_view::{GameboardView, GameboardViewSettings};
mod gameboard;
mod gameboard_controller;
mod gameboard_view;
fn main() {
let opengl = OpenGL::V3_2;
let settings = WindowSettings::new("Sudoku", (640, 480))
.exit_on_esc(true)
.graphics_api(opengl)
.vsync(true);
let mut window: GlutinWindow = settings.build().expect("could not create window");
let mut events = Events::new(EventSettings::new().lazy(true));
let mut gl = GlGraphics::new(opengl);
let args: Vec<_> = std::env::args().collect();
let infile = args.get(1).expect("usage: sudoku <sdm-file>");
let gameboard = Gameboard::load_sdm(infile);
let mut gameboard_controller = GameboardController::new(gameboard);
let gameboard_view_settings = GameboardViewSettings::new();
let gameboard_view = GameboardView::new(gameboard_view_settings);
let texture_settings = TextureSettings::new().filter(Filter::Nearest);
let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings)
.expect("Could not load font");
while let Some(e) = events.next(&mut window) {
gameboard_controller.event(
gameboard_view.settings.position,
gameboard_view.settings.size,
&e,
);
if let Some(args) = e.render_args() {
gl.draw(args.viewport(), |c, g| {
use graphics::clear;
clear([1.0; 4], g);
gameboard_view.draw(&gameboard_controller, glyphs, &c, g);
})
}
}
}

View File

@@ -0,0 +1 @@
517962483236847915498351762371695248654218397829734156765129834142583679983476520

View File

@@ -0,0 +1 @@
016400000200009000400000062070230100100000003003087040960000005000800007000006820

View File

@@ -1,11 +1,3 @@
/custom_types/* /out/
/hello/* /*.d/out
/primitives/* /*.d/**/out
/variable_bindings/*
/types/*
!/custom_types/*.rs
!/hello/*.rs
!/primitives/*.rs
!/variable_bindings/*.rs
!/types/*.rs

View File

@@ -0,0 +1,18 @@
use std::convert::From;
#[allow(dead_code)]
#[derive(Debug)]
struct Number {
value: i32,
}
impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}
fn main() {
let num = Number::from(30);
println!("My number is {:?}", num);
}

View File

@@ -0,0 +1,19 @@
use std::convert::Into;
#[allow(dead_code)]
#[derive(Debug)]
struct Number {
value: i32,
}
impl Into<Number> for i32 {
fn into(self) -> Number {
Number { value: self }
}
}
fn main() {
let int = 5;
let num: Number = int.into();
println!("My number is {:?}", num);
}

View File

@@ -0,0 +1,16 @@
use std::fmt;
struct Circle {
radius: i32,
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}
fn main() {
let circle = Circle { radius: 6 };
println!("{}", circle.to_string());
}

View File

@@ -0,0 +1,7 @@
fn main() {
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();
let sum = parsed + turbo_parsed;
println!("Sum: {:?}", sum);
}

View File

@@ -0,0 +1,28 @@
use std::convert::TryFrom;
use std::convert::TryInto;
#[derive(Debug, PartialEq)]
struct EvenNumber(i32);
impl TryFrom<i32> for EvenNumber {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value % 2 == 0 {
Ok(EvenNumber(value))
} else {
Err(())
}
}
}
fn main() {
assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
assert_eq!(EvenNumber::try_from(5), Err(()));
let result: Result<EvenNumber, ()> = 8i32.try_into();
assert_eq!(result, Ok(EvenNumber(8)));
let result: Result<EvenNumber, ()> = 5i32.try_into();
assert_eq!(result, Err(()));
}

View File

@@ -1,3 +1,4 @@
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
struct Person { struct Person {
name: String, name: String,

View File

@@ -0,0 +1,25 @@
#[allow(path_statements)]
#[allow(unused_must_use)]
fn main() {
let x = 5;
x;
x + 1;
15;
let x = 5u32;
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
x_cube + x_squared + x
};
let z = {
2 * x;
};
println!("x is {:?}", x);
println!("y is {:?}", y);
println!("z is {:?}", z);
}

View File

@@ -0,0 +1,13 @@
fn main() {
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}

View File

@@ -0,0 +1,13 @@
fn main() {
for n in 1..=100 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}

View File

@@ -0,0 +1,12 @@
fn main() {
let names = vec!["Bob", "Frank", "Ferris"];
for name in names.iter() {
match name {
&"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
println!("names: {:?}", names);
}

View File

@@ -0,0 +1,12 @@
fn main() {
let names = vec!["Bob", "Frank", "Ferris"];
for name in names.into_iter() {
match name {
"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
//println!("names: {:?}", names);
}

View File

@@ -0,0 +1,12 @@
fn main() {
let mut names = vec!["Bob", "Frank", "Ferris"];
for name in names.iter_mut() {
*name = match name {
&mut "Ferris" => "There is a rustacean among us!",
_ => "Hello",
}
}
println!("names: {:?}", names);
}

View File

@@ -0,0 +1,23 @@
fn main() {
let n = 5;
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
let big_n = if n < 10 && n > -10 {
println!(", and is a small number, increase net-fold");
10 * n
} else {
println!(", and is a big number, halve the number");
n / 2
};
println!("{} -> {}", n, big_n);
}

View File

@@ -0,0 +1,25 @@
fn main() {
let number = Some(7);
let letter: Option<i32> = None;
let emoticon: Option<i32> = None;
if let Some(i) = number {
println!("Matched {:?}!", i);
}
if let Some(i) = letter {
println!("Matched {:?}!", i);
} else {
println!("Didn't match a number. Let's go with a letter!");
}
let i_like_letters = false;
if let Some(i) = emoticon {
println!("Matched {:?}!", i);
} else if i_like_letters {
println!("Didn't match a number. Let's go with a letter!");
} else {
println!("I don't like letters. Let's go with an emoticon :)!");
}
}

View File

@@ -0,0 +1,27 @@
enum Foo {
Bar,
Baz,
Qux(u32),
}
fn main() {
let a = Foo::Bar;
let b = Foo::Baz;
let c = Foo::Qux(100);
if let Foo::Bar = a {
println!("a is a foobar");
}
if let Foo::Bar = b {
println!("b is foobar");
}
if let Foo::Qux(value) = c {
println!("c is {}", value);
}
if let Foo::Qux(value @ 100) = c {
println!("c is one hundred (value: {:?})", value);
}
}

View File

@@ -0,0 +1,11 @@
enum Foo {
Bar,
}
fn main() {
let a = Foo::Bar;
if let Foo::Bar = a {
println!("a is foobar");
}
}

View File

@@ -0,0 +1,16 @@
use std::str::FromStr;
fn get_count_item(s: &str) -> (u64, &str) {
let mut it = s.split(' ');
let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
panic!("Can't segment count item pair: '{s}'");
};
let Ok(count) = u64::from_str(count_str) else {
panic!("Can't parse integer: '{count_str}'");
};
(count, item)
}
fn main() {
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));
}

View File

@@ -0,0 +1,18 @@
#![allow(unreachable_code)]
#![allow(unused_labels)]
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
break 'outer;
}
println!("This point will never be reached");
}
println!("Exited the outer loop");
}

Some files were not shown because too many files have changed in this diff Show More