diff --git a/internal/interpreter/funds_stack.go b/internal/interpreter/funds_queue.go similarity index 77% rename from internal/interpreter/funds_stack.go rename to internal/interpreter/funds_queue.go index ca82877c..007c791a 100644 --- a/internal/interpreter/funds_stack.go +++ b/internal/interpreter/funds_queue.go @@ -10,9 +10,9 @@ type Sender struct { Color string } -type stack[T any] struct { +type queue[T any] struct { Head T - Tail *stack[T] + Tail *queue[T] // Instead of keeping a single ref of the lastCell and updating the invariant on every push/pop operation, // we keep a cache of the last cell on every cell. @@ -20,10 +20,10 @@ type stack[T any] struct { // // While, unlike keeping a single reference (like golang's queue `container/list` package does), this is not always O(1), // the amortized time should still be O(1) (the number of steps of traversal while searching the last elem is not higher than the number of .Push() calls) - lastCell *stack[T] + lastCell *queue[T] } -func (s *stack[T]) getLastCell() *stack[T] { +func (s *queue[T]) getLastCell() *queue[T] { // check if this is the last cell without reading cache first if s.Tail == nil { return s @@ -43,11 +43,11 @@ func (s *stack[T]) getLastCell() *stack[T] { return s.lastCell } -func fromSlice[T any](slice []T) *stack[T] { - var ret *stack[T] +func fromSlice[T any](slice []T) *queue[T] { + var ret *queue[T] // TODO use https://pkg.go.dev/slices#Backward in golang 1.23 for i := len(slice) - 1; i >= 0; i-- { - ret = &stack[T]{ + ret = &queue[T]{ Head: slice[i], Tail: ret, } @@ -55,24 +55,24 @@ func fromSlice[T any](slice []T) *stack[T] { return ret } -type fundsStack struct { - senders *stack[Sender] +type fundsQueue struct { + senders *queue[Sender] } -func newFundsStack(senders []Sender) fundsStack { - return fundsStack{ +func newFundsQueue(senders []Sender) fundsQueue { + return fundsQueue{ senders: fromSlice(senders), } } -func (s *fundsStack) compactTop() { +func (s *fundsQueue) compactTop() { for s.senders != nil && s.senders.Tail != nil { first := s.senders.Head second := s.senders.Tail.Head if second.Amount.Cmp(big.NewInt(0)) == 0 { - s.senders = &stack[Sender]{Head: first, Tail: s.senders.Tail.Tail} + s.senders = &queue[Sender]{Head: first, Tail: s.senders.Tail.Tail} continue } @@ -80,7 +80,7 @@ func (s *fundsStack) compactTop() { return } - s.senders = &stack[Sender]{ + s.senders = &queue[Sender]{ Head: Sender{ Name: first.Name, Color: first.Color, @@ -91,7 +91,7 @@ func (s *fundsStack) compactTop() { } } -func (s *fundsStack) PullAll() []Sender { +func (s *fundsQueue) PullAll() []Sender { var senders []Sender for s.senders != nil { senders = append(senders, s.senders.Head) @@ -100,7 +100,7 @@ func (s *fundsStack) PullAll() []Sender { return senders } -func (s *fundsStack) Push(senders ...Sender) { +func (s *fundsQueue) Push(senders ...Sender) { newTail := fromSlice(senders) if s.senders == nil { s.senders = newTail @@ -110,18 +110,18 @@ func (s *fundsStack) Push(senders ...Sender) { } } -func (s *fundsStack) PullAnything(requiredAmount *big.Int) []Sender { +func (s *fundsQueue) PullAnything(requiredAmount *big.Int) []Sender { return s.Pull(requiredAmount, nil) } -func (s *fundsStack) PullColored(requiredAmount *big.Int, color string) []Sender { +func (s *fundsQueue) PullColored(requiredAmount *big.Int, color string) []Sender { return s.Pull(requiredAmount, &color) } -func (s *fundsStack) PullUncolored(requiredAmount *big.Int) []Sender { +func (s *fundsQueue) PullUncolored(requiredAmount *big.Int) []Sender { return s.PullColored(requiredAmount, "") } -func (s *fundsStack) Pull(requiredAmount *big.Int, color *string) []Sender { +func (s *fundsQueue) Pull(requiredAmount *big.Int, color *string) []Sender { // clone so that we can manipulate this arg requiredAmount = new(big.Int).Set(requiredAmount) @@ -136,7 +136,7 @@ func (s *fundsStack) Pull(requiredAmount *big.Int, color *string) []Sender { if color != nil && available.Color != *color { out1 := s.Pull(requiredAmount, color) - s.senders = &stack[Sender]{ + s.senders = &queue[Sender]{ Head: available, Tail: s.senders, } @@ -150,7 +150,7 @@ func (s *fundsStack) Pull(requiredAmount *big.Int, color *string) []Sender { requiredAmount.Sub(requiredAmount, available.Amount) case 1: // more than enough - s.senders = &stack[Sender]{ + s.senders = &queue[Sender]{ Head: Sender{ Name: available.Name, Color: available.Color, @@ -174,15 +174,15 @@ func (s *fundsStack) Pull(requiredAmount *big.Int, color *string) []Sender { return out } -// Clone the stack so that you can safely mutate one without mutating the other -func (s fundsStack) Clone() fundsStack { - fs := newFundsStack(nil) +// Clone the queue so that you can safely mutate one without mutating the other +func (s fundsQueue) Clone() fundsQueue { + fq := newFundsQueue(nil) senders := s.senders for senders != nil { - fs.Push(senders.Head) + fq.Push(senders.Head) senders = senders.Tail } - return fs + return fq } diff --git a/internal/interpreter/funds_stack_test.go b/internal/interpreter/funds_queue_test.go similarity index 72% rename from internal/interpreter/funds_stack_test.go rename to internal/interpreter/funds_queue_test.go index a7a5d6af..87672be9 100644 --- a/internal/interpreter/funds_stack_test.go +++ b/internal/interpreter/funds_queue_test.go @@ -8,11 +8,11 @@ import ( ) func TestEnoughBalance(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(100)}, }) - out := stack.PullAnything(big.NewInt(2)) + out := queue.PullAnything(big.NewInt(2)) require.Equal(t, []Sender{ {Name: "s1", Amount: big.NewInt(2)}, }, out) @@ -20,10 +20,10 @@ func TestEnoughBalance(t *testing.T) { } func TestPush(t *testing.T) { - stack := newFundsStack(nil) - stack.Push(Sender{Name: "acc", Amount: big.NewInt(100)}) + queue := newFundsQueue(nil) + queue.Push(Sender{Name: "acc", Amount: big.NewInt(100)}) - out := stack.PullUncolored(big.NewInt(20)) + out := queue.PullUncolored(big.NewInt(20)) require.Equal(t, []Sender{ {Name: "acc", Amount: big.NewInt(20)}, }, out) @@ -31,107 +31,107 @@ func TestPush(t *testing.T) { } func TestSimple(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(2)}, {Name: "s2", Amount: big.NewInt(10)}, }) - out := stack.PullAnything(big.NewInt(5)) + out := queue.PullAnything(big.NewInt(5)) require.Equal(t, []Sender{ {Name: "s1", Amount: big.NewInt(2)}, {Name: "s2", Amount: big.NewInt(3)}, }, out) - out = stack.PullAnything(big.NewInt(7)) + out = queue.PullAnything(big.NewInt(7)) require.Equal(t, []Sender{ {Name: "s2", Amount: big.NewInt(7)}, }, out) } func TestPullZero(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(2)}, {Name: "s2", Amount: big.NewInt(10)}, }) - out := stack.PullAnything(big.NewInt(0)) + out := queue.PullAnything(big.NewInt(0)) require.Equal(t, []Sender(nil), out) } func TestCompactFunds(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(2)}, {Name: "s1", Amount: big.NewInt(10)}, }) - out := stack.PullAnything(big.NewInt(5)) + out := queue.PullAnything(big.NewInt(5)) require.Equal(t, []Sender{ {Name: "s1", Amount: big.NewInt(5)}, }, out) } func TestCompactFunds3Times(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(2)}, {Name: "s1", Amount: big.NewInt(3)}, {Name: "s1", Amount: big.NewInt(1)}, }) - out := stack.PullAnything(big.NewInt(6)) + out := queue.PullAnything(big.NewInt(6)) require.Equal(t, []Sender{ {Name: "s1", Amount: big.NewInt(6)}, }, out) } func TestCompactFundsWithEmptySender(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(2)}, {Name: "s2", Amount: big.NewInt(0)}, {Name: "s1", Amount: big.NewInt(10)}, }) - out := stack.PullAnything(big.NewInt(5)) + out := queue.PullAnything(big.NewInt(5)) require.Equal(t, []Sender{ {Name: "s1", Amount: big.NewInt(5)}, }, out) } func TestMissingFunds(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(2)}, }) - out := stack.PullAnything(big.NewInt(300)) + out := queue.PullAnything(big.NewInt(300)) require.Equal(t, []Sender{ {Name: "s1", Amount: big.NewInt(2)}, }, out) } func TestNoZeroLeftovers(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(10)}, {Name: "s2", Amount: big.NewInt(15)}, }) - stack.PullAnything(big.NewInt(10)) + queue.PullAnything(big.NewInt(10)) - out := stack.PullAnything(big.NewInt(15)) + out := queue.PullAnything(big.NewInt(15)) require.Equal(t, []Sender{ {Name: "s2", Amount: big.NewInt(15)}, }, out) } func TestReconcileColoredManyDestPerSender(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {"src", big.NewInt(10), "X"}, }) - out := stack.PullColored(big.NewInt(5), "X") + out := queue.PullColored(big.NewInt(5), "X") require.Equal(t, []Sender{ {Name: "src", Amount: big.NewInt(5), Color: "X"}, }, out) - out = stack.PullColored(big.NewInt(5), "X") + out = queue.PullColored(big.NewInt(5), "X") require.Equal(t, []Sender{ {Name: "src", Amount: big.NewInt(5), Color: "X"}, }, out) @@ -139,7 +139,7 @@ func TestReconcileColoredManyDestPerSender(t *testing.T) { } func TestPullColored(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(5)}, {Name: "s2", Amount: big.NewInt(1), Color: "red"}, {Name: "s3", Amount: big.NewInt(10)}, @@ -147,7 +147,7 @@ func TestPullColored(t *testing.T) { {Name: "s5", Amount: big.NewInt(5)}, }) - out := stack.PullColored(big.NewInt(2), "red") + out := queue.PullColored(big.NewInt(2), "red") require.Equal(t, []Sender{ {Name: "s2", Amount: big.NewInt(1), Color: "red"}, {Name: "s4", Amount: big.NewInt(1), Color: "red"}, @@ -158,16 +158,16 @@ func TestPullColored(t *testing.T) { {Name: "s3", Amount: big.NewInt(10)}, {Name: "s4", Amount: big.NewInt(1), Color: "red"}, {Name: "s5", Amount: big.NewInt(5)}, - }, stack.PullAll()) + }, queue.PullAll()) } func TestPullColoredComplex(t *testing.T) { - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {"s1", big.NewInt(1), "c1"}, {"s2", big.NewInt(1), "c2"}, }) - out := stack.PullColored(big.NewInt(1), "c2") + out := queue.PullColored(big.NewInt(1), "c2") require.Equal(t, []Sender{ {Name: "s2", Amount: big.NewInt(1), Color: "c2"}, }, out) @@ -175,13 +175,13 @@ func TestPullColoredComplex(t *testing.T) { func TestClone(t *testing.T) { - fs := newFundsStack([]Sender{ + fq := newFundsQueue([]Sender{ {"s1", big.NewInt(10), ""}, }) - cloned := fs.Clone() + cloned := fq.Clone() - fs.PullAll() + fq.PullAll() require.Equal(t, []Sender{ {"s1", big.NewInt(10), ""}, @@ -192,19 +192,19 @@ func TestClone(t *testing.T) { func TestCompactFundsAndPush(t *testing.T) { noCol := "" - stack := newFundsStack([]Sender{ + queue := newFundsQueue([]Sender{ {Name: "s1", Amount: big.NewInt(2)}, {Name: "s1", Amount: big.NewInt(10)}, }) - stack.Pull(big.NewInt(1), &noCol) + queue.Pull(big.NewInt(1), &noCol) - stack.Push(Sender{ + queue.Push(Sender{ Name: "pushed", Amount: big.NewInt(42), }) - out := stack.PullAll() + out := queue.PullAll() require.Equal(t, []Sender{ {Name: "s1", Amount: big.NewInt(11)}, {Name: "pushed", Amount: big.NewInt(42)}, diff --git a/internal/interpreter/interpreter.go b/internal/interpreter/interpreter.go index 64ff651f..04c94bdf 100644 --- a/internal/interpreter/interpreter.go +++ b/internal/interpreter/interpreter.go @@ -294,7 +294,7 @@ func RunProgram( SetAccountsMeta: AccountsMetadata{}, Store: store, Postings: make([]Posting, 0), - fundsStack: newFundsStack(nil), + fundsQueue: newFundsQueue(nil), CurrentBalanceQuery: BalanceQuery{}, ctx: ctx, @@ -358,7 +358,7 @@ type programState struct { ParsedVars map[string]Value TxMeta map[string]Value Postings []Posting - fundsStack fundsStack + fundsQueue fundsQueue Store Store @@ -380,7 +380,7 @@ func (st *programState) pushSender(name string, monetary *big.Int, color string) balance := st.CachedBalances.fetchBalance(name, st.CurrentAsset, color) balance.Sub(balance, monetary) - st.fundsStack.Push(Sender{Name: name, Amount: monetary, Color: color}) + st.fundsQueue.Push(Sender{Name: name, Amount: monetary, Color: color}) } // Append a posting without checking if account has enough balance. @@ -407,7 +407,7 @@ func (st *programState) pushReceiver(name string, monetary *big.Int) { return } - senders := st.fundsStack.PullAnything(monetary) + senders := st.fundsQueue.PullAnything(monetary) for _, sender := range senders { postings := Posting{ @@ -729,11 +729,11 @@ func (s *programState) trySendingToAccount(accountLiteral parser.ValueExpr, amou } func (s *programState) cloneState() func() { - fsBackup := s.fundsStack.Clone() + fqBackup := s.fundsQueue.Clone() balancesBackup := s.CachedBalances.DeepClone() return func() { - s.fundsStack = fsBackup + s.fundsQueue = fqBackup s.CachedBalances = balancesBackup } } @@ -822,7 +822,7 @@ func (s *programState) trySendingUpTo(source parser.Source, amount *big.Int) (*b leadingSources := source.Sources[0 : len(source.Sources)-1] for _, source := range leadingSources { - // do not move this line below (as .trySendingUpTo() will mutate the fundsStack) + // do not move this line below (as .trySendingUpTo() will mutate the fundsQueue) undo := s.cloneState() sentAmt, err := s.trySendingUpTo(source, amount)