From e05c1fd7883189628d2e879c6405d22459e73fa3 Mon Sep 17 00:00:00 2001 From: Drew Bednar Date: Sun, 22 Sep 2024 13:32:52 -0400 Subject: [PATCH] Added more vector clock functionality --- clock/clock.go | 42 +++++++++++++++++++----- clock/clock_test.go | 79 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 17 deletions(-) diff --git a/clock/clock.go b/clock/clock.go index ab59049..4ab1f65 100644 --- a/clock/clock.go +++ b/clock/clock.go @@ -1,8 +1,20 @@ package clock +import ( + "errors" +) + +func max(x, y uint) uint { + if x >= y { + return x + } else { + return y + } +} + // VectorClock type is used for determining the partial ordering of events in a distributed system. type VectorClock struct { - clock []int + clock []uint } // GetClock returns a copy of the internal vector clock. @@ -16,12 +28,26 @@ type VectorClock struct { // // []int: A copy of the internal vector clock slice, where each element // represents the logical time for a corresponding process. -func (vc *VectorClock) GetClock() []int { - clock := make([]int, len(vc.clock)) +func (vc *VectorClock) GetClock() []uint { + clock := make([]uint, len(vc.clock)) copy(clock, vc.clock) return clock } +func (vc *VectorClock) Sync(v VectorClock) ([]uint, error) { + compClock := v.GetClock() + + if len(vc.clock) != len(compClock) { + return nil, errors.New("VectorClocks are of different lengths.") + } + + for i := range vc.clock { + vc.clock[i] = max(vc.clock[i], compClock[i]) + } + + return vc.GetClock(), nil +} + // NewVectorClock creates a new VectorClock initialized to zero. // // Parameters: @@ -33,9 +59,9 @@ func (vc *VectorClock) GetClock() []int { // // *VectorClock: A pointer to the newly created VectorClock instance, with all // elements of the clock initialized to zero. -func NewVectorClock(size int) *VectorClock { +func NewVectorClock(size uint) *VectorClock { return &VectorClock{ - clock: make([]int, size), + clock: make([]uint, size), } } @@ -47,15 +73,15 @@ func NewVectorClock(size int) *VectorClock { // // Parameters: // -// a []int: An array of integers representing logical times for each process. +// a []uint: An array of integers representing logical times for each process. // // Returns: // // *VectorClock: A pointer to a newly created VectorClock instance where the // internal clock is a copy of the provided array. -func ArrayToVectorClock(a []int) *VectorClock { +func ArrayToVectorClock(a []uint) *VectorClock { vc := &VectorClock{ - clock: make([]int, len(a)), + clock: make([]uint, len(a)), } copy(vc.clock, a) return vc diff --git a/clock/clock_test.go b/clock/clock_test.go index 4aa48fb..d41f911 100644 --- a/clock/clock_test.go +++ b/clock/clock_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func assertClocksEqual(t testing.TB, got, want []int) { +func assertClocksEqual(t testing.TB, got, want []uint) { t.Helper() if !reflect.DeepEqual(got, want) { @@ -15,8 +15,69 @@ func assertClocksEqual(t testing.TB, got, want []int) { } func TestVectorClock(t *testing.T) { + + t.Run("start value sync", func(t *testing.T) { + testCases := []struct { + a []uint + b []uint + expected []uint + }{ + {[]uint{0, 0}, []uint{0, 0}, []uint{0, 0}}, + {[]uint{2, 0}, []uint{0, 2}, []uint{2, 2}}, + {[]uint{4, 11}, []uint{3, 10}, []uint{4, 11}}, + {[]uint{5, 9}, []uint{8, 12}, []uint{8, 12}}, + {[]uint{1, 1}, []uint{1, 1}, []uint{1, 1}}, + // {[]uint{math.MaxUint32, 1}, []uint{(math.MaxUint32 + 1), 1}, []uint{1, 1}}, + } + + for _, tc := range testCases { + vc := VectorClock{clock: tc.a} + + clock, err := vc.Sync(VectorClock{clock: tc.b}) + + if err != nil { + t.Errorf("Sync should not have errored") + } + + assertClocksEqual(t, clock, tc.expected) + } + + }) + + t.Run("basic sync 2", func(t *testing.T) { + vc := VectorClock{clock: []uint{4, 11}} + + clock, err := vc.Sync(VectorClock{clock: []uint{4, 10}}) + + if err != nil { + t.Errorf("Sync should not have errored") + } + + assertClocksEqual(t, clock, []uint{4, 11}) + + }) + + t.Run("basic sync", func(t *testing.T) { + + }) + t.Run("basic sync", func(t *testing.T) { + + }) + vc := VectorClock{clock: []uint{2, 0}} + + clock, err := vc.Sync(VectorClock{clock: []uint{0, 2}}) + + if err != nil { + t.Errorf("Sync should not have errored") + } + + assertClocksEqual(t, clock, []uint{2, 2}) + +} + +func TestNewVectorClock(t *testing.T) { got := NewVectorClock(2) - want := VectorClock{clock: []int{0, 0}} + want := VectorClock{clock: []uint{0, 0}} assertClocksEqual(t, got.GetClock(), want.GetClock()) @@ -25,9 +86,9 @@ func TestVectorClock(t *testing.T) { func TestArrayToVectorClock(t *testing.T) { t.Run("from empty slice", func(t *testing.T) { - got := ArrayToVectorClock([]int{}) + got := ArrayToVectorClock([]uint{}) want := VectorClock{ - clock: []int{}, + clock: []uint{}, } if len(got.GetClock()) != 0 { @@ -40,9 +101,9 @@ func TestArrayToVectorClock(t *testing.T) { }) t.Run("from zero slice", func(t *testing.T) { - got := ArrayToVectorClock(make([]int, 6)) + got := ArrayToVectorClock(make([]uint, 6)) want := VectorClock{ - clock: []int{0, 0, 0, 0, 0, 0}, + clock: []uint{0, 0, 0, 0, 0, 0}, } assertClocksEqual(t, got.GetClock(), want.GetClock()) @@ -57,17 +118,17 @@ func TestArrayToVectorClock(t *testing.T) { t.Run("from a populated clock", func(t *testing.T) { want := VectorClock{ - clock: []int{2, 3}, + clock: []uint{2, 3}, } got := ArrayToVectorClock(want.GetClock()) assertClocksEqual(t, got.GetClock(), want.GetClock()) }) t.Run("from a slice that is then modified", func(t *testing.T) { - initial_a := []int{1, 2, 3, 4} + initial_a := []uint{1, 2, 3, 4} got := ArrayToVectorClock(initial_a) - want := make([]int, len(initial_a)) + want := make([]uint, len(initial_a)) copy(want, initial_a) initial_a[0] = 12