Skip to content

Commit

Permalink
update xtime library
Browse files Browse the repository at this point in the history
  • Loading branch information
its-felix committed Nov 19, 2024
1 parent afcafa8 commit a344076
Show file tree
Hide file tree
Showing 17 changed files with 528 additions and 254 deletions.
6 changes: 3 additions & 3 deletions go/api/data/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func (h *Handler) QuerySchedules(ctx context.Context, opts ...QueryScheduleOptio
if idx == -1 {
acc[fn] = append(acc[fn], rr)
} else {
existingRRs[idx].Range[0] = common.Min(existingRRs[idx].Range[0], rr.Range[0])
existingRRs[idx].Range[1] = common.Max(existingRRs[idx].Range[1], rr.Range[1])
existingRRs[idx].Range[0] = min(existingRRs[idx].Range[0], rr.Range[0])
existingRRs[idx].Range[1] = max(existingRRs[idx].Range[1], rr.Range[1])
}
} else {
acc[fn] = append(acc[fn], rr)
Expand Down Expand Up @@ -73,7 +73,7 @@ func (h *Handler) QuerySchedules(ctx context.Context, opts ...QueryScheduleOptio
continue
}

if cnt, span := variant.Ranges.Span(); cnt > 0 {
if span, ok := variant.Ranges.Span(); ok {
accumulate(acc, fn, RouteAndRange{
DepartureAirport: variant.Data.DepartureAirport,
ArrivalAirport: variant.Data.ArrivalAirport,
Expand Down
4 changes: 2 additions & 2 deletions go/api/data/query_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func WithIgnoreCodeShares() QueryScheduleOption {
func WithMinDepartureTime(minDepartureTime time.Time) QueryScheduleOption {
return func(o *querySchedulesOptions) error {
o.variantPredicates = append(o.variantPredicates, func(fs *common.FlightSchedule, fsv *common.FlightScheduleVariant) bool {
if cnt, span := fsv.Ranges.Span(); cnt > 0 {
if span, ok := fsv.Ranges.Span(); ok {
return fsv.DepartureTime(span[1]).After(minDepartureTime)
}

Expand All @@ -211,7 +211,7 @@ func WithMinDepartureTime(minDepartureTime time.Time) QueryScheduleOption {
func WithMaxDepartureTime(maxDepartureTime time.Time) QueryScheduleOption {
return func(o *querySchedulesOptions) error {
o.variantPredicates = append(o.variantPredicates, func(fs *common.FlightSchedule, fsv *common.FlightScheduleVariant) bool {
if cnt, span := fsv.Ranges.Span(); cnt > 0 {
if span, ok := fsv.Ranges.Span(); ok {
return fsv.DepartureTime(span[0]).Before(maxDepartureTime)
}

Expand Down
6 changes: 4 additions & 2 deletions go/api/search/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ func findConnections(
}, 0)

currDate := xtime.NewLocalDate(minDeparture.UTC())
for currDate.Compare(xtime.NewLocalDate(maxDeparture.UTC())) <= 0 {
maxDate := xtime.NewLocalDate(maxDeparture.UTC())

for currDate <= maxDate {
for _, origin := range origins {
d := common.Departure{
Airport: origin,
Expand Down Expand Up @@ -184,7 +186,7 @@ func findConnections(
}
}

currDate = currDate.Next()
currDate += 1
}

for _, w := range working {
Expand Down
4 changes: 2 additions & 2 deletions go/api/search/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (fr *FlightRepo) Flights(ctx context.Context, start, end xtime.LocalDate) (
g, ctx := errgroup.WithContext(ctx)
curr := start

for curr.Compare(end) <= 0 {
for curr <= end {
d := curr
g.Go(func() error {
flights, err := fr.flightsInternal(ctx, d)
Expand All @@ -52,7 +52,7 @@ func (fr *FlightRepo) Flights(ctx context.Context, start, end xtime.LocalDate) (
return nil
})

curr = curr.Next()
curr += 1
}

return result, g.Wait()
Expand Down
2 changes: 1 addition & 1 deletion go/common/flight_schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (fs *FlightSchedule) DeleteAll(fn func(*FlightScheduleVariant, xtime.LocalD
return fn(fsv, d)
})

return len(fsv.Ranges) < 1
return fsv.Ranges.Empty()
})
}

Expand Down
167 changes: 167 additions & 0 deletions go/common/xtime/bitset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package xtime

import (
"encoding/base64"
"encoding/json"
"math/big"
)

type b64EncodedBigInt big.Int

func (b *b64EncodedBigInt) UnmarshalText(text []byte) error {
bytes, err := base64.RawURLEncoding.AppendDecode(make([]byte, 0), text)
if err != nil {
return err
}

*b = b64EncodedBigInt(*new(big.Int).SetBytes(bytes))

return nil
}

func (b b64EncodedBigInt) MarshalText() ([]byte, error) {
return base64.RawURLEncoding.AppendEncode(make([]byte, 0), (*big.Int)(&b).Bytes()), nil
}

type LocalDateBitSet struct {
offset LocalDate
bitset big.Int
}

func (bs *LocalDateBitSet) UnmarshalJSON(b []byte) error {
var temp struct {
Offset LocalDate `json:"offset"`
Bitset b64EncodedBigInt `json:"bitset"`
}

if err := json.Unmarshal(b, &temp); err != nil {
return err
}

bs.offset = temp.Offset
bs.bitset = big.Int(temp.Bitset)
return nil
}

func (bs LocalDateBitSet) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Offset LocalDate `json:"offset"`
Bitset b64EncodedBigInt `json:"bitset"`
}{
Offset: bs.offset,
Bitset: b64EncodedBigInt(bs.bitset),
})
}

func (bs LocalDateBitSet) Iter(yield func(LocalDate) bool) {
var zero big.Int

currD := bs.offset
currBs := new(big.Int).Set(&bs.bitset)

for currBs.Cmp(&zero) != 0 {
gap := currBs.TrailingZeroBits()
currD += LocalDate(gap)

if !yield(currD) {
return
}

currBs.Rsh(currBs, gap+1)
currD += 1
}
}

func (bs LocalDateBitSet) Compact() LocalDateBitSet {
gap := bs.bitset.TrailingZeroBits()
if gap == 0 {
return bs
}

bs.offset += LocalDate(gap)
bs.bitset = *new(big.Int).Set(&bs.bitset)
bs.bitset.Rsh(&bs.bitset, gap)

return bs
}

func (bs LocalDateBitSet) Add(d LocalDate) LocalDateBitSet {
index := bs.offset.DaysUntil(d)
bs.bitset = *new(big.Int).Set(&bs.bitset)

if index >= 0 {
bs.bitset.SetBit(&bs.bitset, index, 1)
} else {
bs.offset = d
bs.bitset.Lsh(&bs.bitset, uint(-index))
bs.bitset.SetBit(&bs.bitset, 0, 1)
}

return bs
}

func (bs LocalDateBitSet) Remove(d LocalDate) LocalDateBitSet {
index := bs.offset.DaysUntil(d)
if index < 0 {
return bs
}

bs.bitset = *new(big.Int).Set(&bs.bitset)
bs.bitset.SetBit(&bs.bitset, index, 0)

return bs
}

func (bs LocalDateBitSet) Or(other LocalDateBitSet) LocalDateBitSet {
gap := bs.offset.DaysUntil(other.offset)
if gap < 0 {
gap = -gap
bs, other = other, bs
}

aligned := new(big.Int).Set(&other.bitset)
aligned.Lsh(aligned, uint(gap))

bs.bitset = *new(big.Int).Set(&bs.bitset)
bs.bitset.Or(&bs.bitset, aligned)

return bs
}

func (bs LocalDateBitSet) Contains(d LocalDate) bool {
index := bs.offset.DaysUntil(d)
if index < 0 {
return false
}

return bs.bitset.Bit(index) > 0
}

func (bs LocalDateBitSet) Span() (LocalDateRange, bool) {
firstIndex := int(bs.bitset.TrailingZeroBits())
lastIndex := bs.bitset.BitLen()

if lastIndex < 1 {
return LocalDateRange{}, false
}

return LocalDateRange{
bs.offset + LocalDate(firstIndex),
bs.offset + LocalDate(lastIndex-1),
}, true
}

func (bs LocalDateBitSet) Count() int {
cnt := 0
for i := 0; i < bs.bitset.BitLen(); i++ {
if bs.bitset.Bit(i) > 0 {
cnt++
}
}

return cnt
}

func (bs LocalDateBitSet) Empty() bool {
return bs.bitset.BitLen() < 1
}
113 changes: 113 additions & 0 deletions go/common/xtime/bitset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package xtime

import (
"github.com/stretchr/testify/assert"
"math/big"
"slices"
"testing"
)

func TestLocalDateBitSet_AddIter(t *testing.T) {
var bs LocalDateBitSet
bs = bs.Add(MustParseLocalDate("2024-01-01"))
bs = bs.Add(MustParseLocalDate("2024-06-01"))

assert.Equal(
t,
[]LocalDate{
MustParseLocalDate("2024-01-01"),
MustParseLocalDate("2024-06-01"),
},
slices.Sorted(bs.Iter),
)
}

func TestLocalDateBitSet_RemoveIter(t *testing.T) {
var bs LocalDateBitSet
bs = bs.Add(MustParseLocalDate("2024-01-01"))
bs = bs.Add(MustParseLocalDate("2024-06-01"))
bs = bs.Remove(MustParseLocalDate("2024-01-01"))

assert.Equal(
t,
[]LocalDate{
MustParseLocalDate("2024-06-01"),
},
slices.Sorted(bs.Iter),
)
}

func TestLocalDateBitSet_Compact(t *testing.T) {
bs := LocalDateBitSet{
offset: MustParseLocalDate("2024-01-01"),
bitset: *big.NewInt(0b10),
}

bs = bs.Compact()

assert.Equal(t, MustParseLocalDate("2024-01-02"), bs.offset)
assert.Equal(t, int64(1), bs.bitset.Int64())
}

func TestLocalDateBitSet_Or(t *testing.T) {
var bs1 LocalDateBitSet
bs1 = bs1.Add(MustParseLocalDate("2024-01-01"))

var bs2 LocalDateBitSet
bs2 = bs2.Add(MustParseLocalDate("2024-06-01"))

bs := bs1.Or(bs2)

assert.Equal(
t,
[]LocalDate{
MustParseLocalDate("2024-01-01"),
MustParseLocalDate("2024-06-01"),
},
slices.Sorted(bs.Iter),
)
}

func TestLocalDateBitSet_Contains(t *testing.T) {
var bs LocalDateBitSet
bs = bs.Add(MustParseLocalDate("2024-01-01"))

assert.True(t, bs.Contains(MustParseLocalDate("2024-01-01")))
assert.False(t, bs.Contains(MustParseLocalDate("2024-06-01")))
}

func TestLocalDateBitSet_SpanCount(t *testing.T) {
var bs LocalDateBitSet

span, ok := bs.Span()
assert.False(t, ok)
assert.Equal(t, 0, bs.Count())

bs = bs.Add(MustParseLocalDate("2024-01-01"))
span, ok = bs.Span()
assert.True(t, ok)
assert.Equal(
t,
LocalDateRange{MustParseLocalDate("2024-01-01"), MustParseLocalDate("2024-01-01")},
span,
)
assert.Equal(t, 1, bs.Count())

bs = bs.Add(MustParseLocalDate("2024-06-01"))
span, ok = bs.Span()
assert.True(t, ok)
assert.Equal(
t,
LocalDateRange{MustParseLocalDate("2024-01-01"), MustParseLocalDate("2024-06-01")},
span,
)
assert.Equal(t, 2, bs.Count())
}

func TestLocalDateBitSet_Empty(t *testing.T) {
var bs LocalDateBitSet
assert.True(t, bs.Empty())

bs = bs.Add(MustParseLocalDate("2024-01-01"))
assert.False(t, bs.Empty())
}
Loading

0 comments on commit a344076

Please sign in to comment.