Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions internal/transducer/booking.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package transducer

func NewBookingTransducer(setupConfig *Config, childTransducers ...Transducer) *Transducer {
transitionTable := TransitionTable{
{Idle, Book}: func() *Outputs {
return CreateOutputs().SetState(Reserved).
AddEffect(UpdateBookingStatus).
AddEffect(EmailUser)
},

{Reserved, Book}: func() *Outputs {
return CreateOutputs().SetState(Booked).
AddEffect(UpdateBookingStatus).
AddEffect(EmailUser).
AddEffect(CallClient).
AddEffect(SMSUser)
},

{Reserved, Cancel}: func() *Outputs {
return CreateOutputs().SetState(Cancelled).
AddEffect(UpdateBookingStatus).
AddEffect(EmailUser)
},

{Booked, CheckIn}: func() *Outputs {
return CreateOutputs().SetState(CheckedIn).
AddEffect(UpdateBookingStatus).
AddEffect(EmailClient)
},

{CheckedIn, CheckOut}: func() *Outputs {
return CreateOutputs().SetState(CheckedOut).
AddEffect(UpdateBookingStatus).
AddEffect(EmailClient)
},
}

return &Transducer{
Name: BookingTransducerName,
TransitionTable: transitionTable,
}
}

func NewBookingMachine(BookStateStr string) (*Config, *Transducer) {
BookState := State(BookStateStr)
BookConfig := CreateConfig().SetState(BookState)
BookTransducer := NewBookingTransducer(BookConfig)

return BookConfig, BookTransducer
}
45 changes: 45 additions & 0 deletions internal/transducer/booking_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package transducer

import (
"database-concurrency/internal/transducer/graph"
"fmt"
"testing"
)

func TestRehearseBookingTransducer(t *testing.T) {
bookingConfig := CreateConfig().SetState(Idle)
bookingTransducer := NewBookingTransducer(bookingConfig)

transitionQuery, aggregateQuery := bookingTransducer.ToSQL(Idle)
t.Logf("Transition Query: %v\n", transitionQuery)
t.Logf("Aggregate Query: %v\n", aggregateQuery)

digraph := bookingTransducer.ToDiGraph()
t.Logf("Digraph: %v\n", digraph)
}

func TestBookingShortestPaths(t *testing.T) {
characterConfig := CreateConfig().SetState(Idle)
characterTransducer := NewBookingTransducer(characterConfig)

paths, edges := characterTransducer.GetShortestPaths()
currentState := graph.Vertex(Idle)

for _, path := range paths {
for j, state := range path {
if path[0] != currentState {
stateLog := fmt.Sprintf("\n'%v' state", path[0])
currentState = path[0]
fmt.Println(stateLog)
}

if j < len(path)-1 {
nextState := path[j+1]

input := edges[state][nextState]
transitionLog := fmt.Sprintf("\t\t%-30v + %-30v -> %-30v", state, input, nextState)
fmt.Println(transitionLog)
}
}
}
}
57 changes: 57 additions & 0 deletions internal/transducer/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package transducer

type Config struct {
State State
Data interface{}
Metadata Metadata
}
type Metadata struct {
ChildConfig map[string]*Config
ParallelConfigs []*Config
Test func() error
}

func CreateConfig() *Config {
return &Config{}
}

func (c *Config) GetState() State {
return c.State
}

func (c *Config) GetChildState(name string) State {
if c.Metadata.ChildConfig != nil {
_, exists := c.Metadata.ChildConfig[name]
if exists {
return c.Metadata.ChildConfig[name].State
}
}
return State(Invalid)
}

func (c *Config) SetState(state State) *Config {
c.State = state
return c
}

func (c *Config) SetData(data interface{}) *Config {
c.Data = data
return c
}

func (c *Config) SetMetadata(metadata Metadata) *Config {
c.Metadata = metadata
return c
}

func (c *Config) SetChildConfig(name string, config *Config) *Config {
if c.Metadata.ChildConfig == nil {
c.Metadata.ChildConfig = map[string]*Config{}
}
_, exists := c.Metadata.ChildConfig[name]
if !exists {
c.Metadata.ChildConfig[name] = CreateConfig()
}
c.Metadata.ChildConfig[name] = config
return c
}
37 changes: 37 additions & 0 deletions internal/transducer/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package transducer

// States
const (
// an invalid state for empty states
Invalid State = "Invalid"

Idle State = "Idle"
Reserved State = "Reserved"
Booked State = "Booked"
Cancelled State = "Cancelled"
CheckedIn State = "CheckedIn"
CheckedOut State = "CheckedOut"
)

// Inputs
const (
Reserve Input = "Reserve"
Book Input = "Book"
Cancel Input = "Cancel"
CheckIn Input = "CheckIn"
CheckOut Input = "CheckOut"
)

// Effects
const (
UpdateBookingStatus Effect = iota
EmailUser
EmailClient
CallClient
SMSUser
)

// Names
const (
BookingTransducerName = "booking"
)
101 changes: 101 additions & 0 deletions internal/transducer/graph/graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package graph

import "math"

type GraphSpec interface {
Vertices() []Vertex
Neighbors(v Vertex) []Vertex
Weight(u, v Vertex) float64
}

type Vertex string

type Graph struct {
vertexes []Vertex
edges map[Vertex]map[Vertex]float64
}

func NewGraph(vertexes []Vertex, edges map[Vertex]map[Vertex]float64) Graph {
return Graph{vertexes, edges}
}

func (g Graph) AddEdge(from Vertex, to Vertex, weight float64) {
if _, ok := g.edges[from]; !ok {
g.edges[from] = make(map[Vertex]float64)
}
g.edges[from][to] = weight
}

func (g Graph) Vertices() []Vertex {
return g.vertexes
}

func (g Graph) Neighbors(v Vertex) (vs []Vertex) {
for k := range g.edges[v] {
vs = append(vs, k)
}
return vs
}

func (g Graph) Weight(u, v Vertex) float64 {
return g.edges[u][v]
}

var Infinity = math.Inf(0)

func FloydWarshall(g Graph) (dist map[Vertex]map[Vertex]float64, next map[Vertex]map[Vertex]*Vertex) {
vertexes := g.Vertices()
dist = make(map[Vertex]map[Vertex]float64)
next = make(map[Vertex]map[Vertex]*Vertex)

for _, u := range vertexes {
dist[u] = make(map[Vertex]float64)
next[u] = make(map[Vertex]*Vertex)
for _, v := range vertexes {
dist[u][v] = Infinity
}
dist[u][u] = 0
for _, v := range g.Neighbors(u) {
v := v
dist[u][v] = g.Weight(u, v)
next[u][v] = &v
}
}

for _, k := range vertexes {
for _, i := range vertexes {
for _, j := range vertexes {
if dist[i][k] < Infinity && dist[k][j] < Infinity {
if dist[i][j] > dist[i][k]+dist[k][j] {
dist[i][j] = dist[i][k] + dist[k][j]
next[i][j] = next[i][k]
}
}
}
}
}
return dist, next
}

func GetPaths(u Vertex, v Vertex, next map[Vertex]map[Vertex]*Vertex) (paths []Vertex) {
if next[u][v] == nil {
return
}
paths = []Vertex{u}
for u != v {
u = *next[u][v]
paths = append(paths, u)
}
return paths
}

func PrintPaths(vv []Vertex) (s string) {
if len(vv) == 0 {
return ""
}
s = string(vv[0])
for _, v := range vv[1:] {
s += " -> " + string(v)
}
return s
}
64 changes: 64 additions & 0 deletions internal/transducer/outputs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package transducer

type Outputs struct {
Config *Config
Effects []Effect
}

func CreateOutputs() *Outputs {
return &Outputs{Config: &Config{}}
}

func (o *Outputs) GetState() State {
if o.Config != nil {
return o.Config.State
}
return State(Invalid)
}

func (o *Outputs) GetChildState(name string) State {
if o.Config.Metadata.ChildConfig != nil {
_, exists := o.Config.Metadata.ChildConfig[name]
if exists {
return o.Config.Metadata.ChildConfig[name].State
}
}
return State(Invalid)
}

func (o *Outputs) SetState(state State) *Outputs {
o.Config.SetState(state)
return o
}

func (o *Outputs) SetData(data interface{}) *Outputs {
o.Config.Data = data
return o
}

func (o *Outputs) SetMetadata(metadata Metadata) *Outputs {
o.Config.Metadata = metadata
return o
}

func (o *Outputs) AddEffect(effect Effect) *Outputs {
o.Effects = append(o.Effects, effect)
return o
}

func (o *Outputs) AddEffects(effects []Effect) *Outputs {
o.Effects = append(o.Effects, effects...)
return o
}

func (o *Outputs) TransduceChild(transducer Transducer, config *Config, input Input) *Outputs {
if config.Metadata.ChildConfig != nil {
_, exists := config.Metadata.ChildConfig[transducer.Name]
if exists {
innerOutputs := transducer.Transduce(config.Metadata.ChildConfig[transducer.Name], input)
outputs := MergeChildOutputs(o, innerOutputs, transducer.Name)
return outputs
}
}
return o
}
Loading