diff --git a/.idea/dgraph.iml b/.idea/dgraph.iml
new file mode 100644
index 0000000..c956989
--- /dev/null
+++ b/.idea/dgraph.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..15a15b2
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..28a804d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..313f081
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..eb72041
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,230 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/graph-basic.go b/core/graph-basic.go
new file mode 100644
index 0000000..01cd079
--- /dev/null
+++ b/core/graph-basic.go
@@ -0,0 +1,111 @@
+package core
+
+import (
+ "fmt"
+)
+
+func isMultiple(m map[string][]string, id string) bool {
+ list, ok := m[id]
+ return ok && len(list) > 1;
+}
+
+type Set map[string]struct{}
+
+func (s *Set) Add(key string) {
+ (*s)[key] = struct{}{}
+}
+
+func (s *Set) Has(key string) bool {
+ _, ok := (*s)[key]
+ return ok
+}
+
+type relationMap map[string][]string
+type nodesMap map[string]NodeInput
+
+type GraphBasic struct {
+ list []NodeInput
+ nodesMap nodesMap
+ incomesByNodeIdMap relationMap
+ outcomesByNodeIdMap relationMap
+ loopsByNodeIdMap relationMap
+}
+
+func NewGraphBasic(list []NodeInput) (*GraphBasic, error) {
+ g := &GraphBasic{
+ list: list,
+ nodesMap: make(nodesMap),
+ incomesByNodeIdMap: make(relationMap),
+ outcomesByNodeIdMap: make(relationMap),
+ loopsByNodeIdMap: make(relationMap),
+ }
+ for _, node := range g.list {
+ if _, ok := g.nodesMap[node.Id]; ok {
+ return nil, fmt.Errorf("Duplicate node id `%s`", node.Id)
+ }
+ g.nodesMap[node.Id] = node
+ }
+}
+
+func (g *GraphBasic) detectIncomesAndOutcomes() (err error) {
+ totalSet := make(Set)
+ for _, node := range g.list {
+ if totalSet.Has(node.Id) {
+ continue
+ }
+ branchSet := make(Set)
+ _ , err = g.traverseVertically(node, &branchSet, &totalSet)
+ if err != nil {
+ return
+ }
+ }
+}
+
+func (g *GraphBasic) traverseVertically(node NodeInput, branchSet *Set, totalSet *Set) (*Set, error) {
+ if branchSet.Has(node.Id) {
+ return nil, fmt.Errorf("Duplicate incomes for node id `%s`", node.Id)
+ }
+ branchSet.Add(node.Id)
+ totalSet.Add(node.Id)
+ for _, outcomeId := range node.Next {
+ // skip loops which are already detected
+ if g.IsLoopEdge(node.Id, outcomeId) {
+ continue
+ }
+ // detect loops
+ if branchSet.Has(outcomeId) {
+ addUniqueRelation(g.loopsByNodeIdMap, node.Id, outcomeId)
+ }
+ // @TODO check if addUniqueRelation works
+ }
+}
+
+func (g *GraphBasic) IsLoopEdge(nodeId string, outcomeId string) bool {
+ loops, ok := g.loopsByNodeIdMap[nodeId]
+ if !ok {
+ return false
+ }
+ for _, id := range loops {
+ if id == outcomeId {
+ return true
+ }
+ }
+ return false
+}
+
+func addUniqueRelation(rm relationMap, key, value string) {
+ relations, ok := rm[key]
+ if !ok {
+ rm[key] = []string{value}
+ }
+ isUnique := true
+ for _, v := range relations {
+ if v == value {
+ isUnique = false
+ break
+ }
+ }
+ if isUnique {
+ rm[key] = append(relations, value)
+ }
+}
diff --git a/core/matrix.go b/core/matrix.go
new file mode 100644
index 0000000..a02fd7a
--- /dev/null
+++ b/core/matrix.go
@@ -0,0 +1,158 @@
+package core
+
+type Matrix struct {
+ s [][]*NodeOutput
+}
+
+func NewMatrix() *Matrix {
+ return new(Matrix)
+}
+
+func (m *Matrix) Width() (width int) {
+ for _, row := range m.s {
+ if len(row) > width {
+ width = len(row)
+ }
+ }
+ return
+}
+
+func (m *Matrix) Height() int {
+ return len(m.s)
+}
+
+func (m *Matrix) hasHorizontalCollision(x, y int) bool {
+ if len(m.s) == 0 || y >= len(m.s) {
+ return false
+ }
+ row := m.s[y]
+ for _, point := range row {
+ if point != nil && !m.isAllChildrenOnMatrix(point) {
+ return true
+ }
+ }
+ return false
+}
+
+func (m *Matrix) hasVerticalCollision(x, y int) bool {
+ if x >= m.Width() {
+ return false
+ }
+ for index, row := range m.s {
+ if index >= y && x < len(row) && row[x] != nil {
+ return true
+ }
+ }
+ return false
+}
+
+func (m *Matrix) getFreeRowForColumn(x int) int {
+ if m.Height() == 0 {
+ return 0
+ }
+ y := -1
+ for index, row := range m.s {
+ if len(row) == 0 || x >= len(row) || row[x] == nil {
+ y = index
+ break
+ }
+ }
+ if y == -1 {
+ y = m.Height()
+ }
+ return y
+}
+
+func (m *Matrix) extendHeight(toValue int) {
+ for m.Height() < toValue{
+ m.s = append(m.s, make([]*NodeOutput, m.Width()))
+ }
+}
+
+func (m *Matrix) extendWidth(toValue int) {
+ for _, row := range m.s {
+ for len(row) < toValue {
+ row = append(row, nil)
+ }
+ }
+}
+
+func (m *Matrix) insertRowBefore(y int) {
+ row := make([]*NodeOutput, m.Width())
+ m.s = append(m.s[:y], append([][]*NodeOutput{row}, m.s[y:]...)...)
+}
+
+func (m *Matrix) insertColumnBefore(x int) {
+ for _, row := range m.s {
+ row = append(row[:x], append([]*NodeOutput{nil}, row[x:]...)...)
+ }
+}
+
+func (m *Matrix) find(callback func(item *NodeOutput)bool) []int {
+ for y, row := range m.s {
+ for x, point := range row {
+ if point == nil {
+ continue
+ }
+ if callback(point) {
+ return []int{x, y}
+ }
+ }
+ }
+ return nil
+}
+
+type findNodeResult struct {
+ coords []int
+ item *NodeOutput
+}
+
+func (m *Matrix) findNode(callback func(item *NodeOutput)bool) *findNodeResult {
+ for y, row := range m.s {
+ for x, point := range row {
+ if point == nil {
+ continue
+ }
+ if callback(point) {
+ return &findNodeResult{
+ coords: []int{x,y},
+ item: point,
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func (m *Matrix) getByCoords(x, y int) *NodeOutput {
+ if x >= m.Width() || y >= m.Height() {
+ return nil
+ }
+ return m.s[y][x]
+}
+
+func (m *Matrix) insert(x, y int, item *NodeOutput) {
+ if m.Height() <= y {
+ m.extendHeight(y+1)
+ }
+ if m.Width() <= x {
+ m.extendWidth(x+1)
+ }
+ m.s[y][x] = item
+}
+
+func (m *Matrix) isAllChildrenOnMatrix(item *NodeOutput) bool {
+ return len(item.Next) == item.ChildrenOnMatrix
+}
+
+func (m *Matrix) Normalize() map[string]*MatrixNode {
+ acc := make(map[string]*MatrixNode)
+ for y, row := range m.s {
+ for x, item := range row {
+ if item != nil {
+ acc[item.Id] = &MatrixNode{NodeOutput: item, X: x, Y: y}
+ }
+ }
+ }
+ return acc
+}
diff --git a/core/set.go b/core/set.go
new file mode 100644
index 0000000..9a8bc95
--- /dev/null
+++ b/core/set.go
@@ -0,0 +1 @@
+package core
diff --git a/core/traverse-queue.go b/core/traverse-queue.go
new file mode 100644
index 0000000..f808d15
--- /dev/null
+++ b/core/traverse-queue.go
@@ -0,0 +1,81 @@
+package core
+
+type TraverseQueue struct {
+ s []*NodeOutput
+}
+
+func NewTraverseQueue() *TraverseQueue {
+ return new(TraverseQueue)
+}
+
+func (q *TraverseQueue) add(incomeId *string, bufferQueue *TraverseQueue, items ...*NodeInput) {
+ for _ ,itm := range items {
+ item := q.find(func(v *NodeOutput) bool {
+ return v.Id == itm.Id
+ })
+ if item == nil && bufferQueue != nil {
+ item = bufferQueue.find(func(v *NodeOutput) bool {
+ return v.Id == itm.Id
+ })
+ }
+ if item != nil && incomeId != nil {
+ item.PassedIncomes = append(item.PassedIncomes, *incomeId)
+ return
+ }
+ var incomes []string
+ if incomeId != nil {
+ incomes = append(incomes, *incomeId)
+ }
+ q.s = append(q.s, &NodeOutput{
+ NodeInput: &NodeInput{
+ Id: itm.Id,
+ Next: itm.Next,
+ },
+ PassedIncomes: append([]string{}, incomes...),
+ RenderIncomes: append([]string{}, incomes...),
+ ChildrenOnMatrix: 0,
+ })
+ }
+}
+
+func (q *TraverseQueue) find(cb func(v *NodeOutput)bool)*NodeOutput {
+ for _, itm := range q.s {
+ if cb(itm) {
+ return itm
+ }
+ }
+ return nil
+}
+
+func (q *TraverseQueue) push(item *NodeOutput) {
+ q.s = append(q.s, item)
+}
+
+func (q *TraverseQueue) length() int {
+ return len(q.s)
+}
+
+func (q *TraverseQueue) some(cb func(v *NodeOutput)bool) bool {
+ for _, itm := range q.s {
+ if cb(itm) {
+ return true
+ }
+ }
+ return false
+}
+
+func (q *TraverseQueue) shift() *NodeOutput {
+ if len(q.s) == 0 {
+ return nil
+ }
+ item := *q.s[0]
+ q.s = append(q.s, q.s[1:]...)
+ return &item
+}
+
+func (q *TraverseQueue) drain() *TraverseQueue {
+ newQ := NewTraverseQueue()
+ newQ.s = append(newQ.s, q.s...)
+ q.s = []*NodeOutput{}
+ return newQ
+}
diff --git a/core/typings.go b/core/typings.go
new file mode 100644
index 0000000..bae5f73
--- /dev/null
+++ b/core/typings.go
@@ -0,0 +1,57 @@
+package core
+
+type NodeType int
+
+const (
+ NodeTypeUnknown NodeType = iota
+ NodeTypeRootSimple
+ NodeTypeRootSplit
+ NodeTypeSimple
+ NodeTypeSplit
+ NodeTypeJoin
+ NodeTypeSplitJoin
+)
+
+type AnchorType int
+
+const (
+ AnchorUnknown AnchorType = iota
+ AnchorJoin
+ AnchorSplit
+ AnchorLoop
+)
+
+type AnchorMarginType int
+
+const (
+ AnchorMarginNone AnchorMarginType = iota
+ AnchorMarginLeft
+ AnchorMarginRight
+)
+
+type NodeInput struct {
+ Id string
+ Next []string
+}
+
+type NodeOutput struct {
+ *NodeInput
+ *Anchor
+ IsAnchor bool
+ PassedIncomes []string
+ RenderIncomes []string
+ ChildrenOnMatrix int
+}
+
+type Anchor struct {
+ Type AnchorType
+ From string
+ To string
+ Margin AnchorMarginType
+}
+
+type MatrixNode struct {
+ *NodeOutput
+ X int
+ Y int
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..6d27cee
--- /dev/null
+++ b/go.mod
@@ -0,0 +1 @@
+module github.com/lempiy/dgraph
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..fe7f767
--- /dev/null
+++ b/main.go
@@ -0,0 +1,5 @@
+package main
+
+func main() {
+
+}