// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package arrow

import (
	
	
	
	

	
	
	
	
)

type BooleanType struct{}

func ( *BooleanType) () Type            { return BOOL }
func ( *BooleanType) () string        { return "bool" }
func ( *BooleanType) () string      { return "bool" }
func ( *BooleanType) () string { return typeFingerprint() }
func (BooleanType) () int             { return 1 }

// BitWidth returns the number of bits required to store a single element of this data type in memory.
func ( *BooleanType) () int { return 1 }

func (BooleanType) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecBitmap()}}
}

type FixedSizeBinaryType struct {
	ByteWidth int
}

func (*FixedSizeBinaryType) () Type              { return FIXED_SIZE_BINARY }
func (*FixedSizeBinaryType) () string          { return "fixed_size_binary" }
func ( *FixedSizeBinaryType) () int       { return 8 * .ByteWidth }
func ( *FixedSizeBinaryType) () int          { return .ByteWidth }
func ( *FixedSizeBinaryType) () string { return typeFingerprint() }
func ( *FixedSizeBinaryType) () string {
	return "fixed_size_binary[" + strconv.Itoa(.ByteWidth) + "]"
}
func ( *FixedSizeBinaryType) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(.ByteWidth)}}
}

type (
	Timestamp int64
	Time32    int32
	Time64    int64
	TimeUnit  int
	Date32    int32
	Date64    int64
	Duration  int64
)

// Date32FromTime returns a Date32 value from a time object
func ( time.Time) Date32 {
	return Date32(.Truncate(24*time.Hour).Unix() / int64((time.Hour * 24).Seconds()))
}

func ( Date32) () time.Time {
	return time.Unix(0, 0).UTC().AddDate(0, 0, int())
}

func ( Date32) () string {
	return .ToTime().Format("2006-01-02")
}

// Date64FromTime returns a Date64 value from a time object
func ( time.Time) Date64 {
	// truncate to the start of the day to get the correct value
	 = .Truncate(24 * time.Hour)
	return Date64(.Unix()*1e3 + int64(.Nanosecond())/1e6)
}

func ( Date64) () time.Time {
	 := int(int64() / (time.Hour * 24).Milliseconds())
	return time.Unix(0, 0).UTC().AddDate(0, 0, )
}

func ( Date64) () string {
	return .ToTime().Format("2006-01-02")
}

// TimestampFromStringInLocation is like TimestampFromString, but treats the time instant
// as if it were in the provided timezone before converting to UTC for internal representation.
func ( string,  TimeUnit,  *time.Location) (Timestamp, bool, error) {
	if len() < 10 {
		return 0, false, fmt.Errorf("%w: invalid timestamp string", ErrInvalid)
	}

	var (
		         = "2006-01-02"
		        string
		 = len()
	)

	if  > 10 {
		switch {
		case [len()-1] == 'Z':
			 = "Z"
			--
		case [len()-3] == '+' || [len()-3] == '-':
			 = "-07"
			 -= 3
		case [len()-5] == '+' || [len()-5] == '-':
			 = "-0700"
			 -= 5
		case [len()-6] == '+' || [len()-6] == '-':
			 = "-07:00"
			 -= 6
		}
	}

	switch {
	case  == 13:
		 += string([10]) + "15"
	case  == 16:
		 += string([10]) + "15:04"
	case  >= 19:
		 += string([10]) + "15:04:05.999999999"
	}

	// error if we're truncating precision
	// don't need a case for nano as time.Parse will already error if
	// more than nanosecond precision is provided
	switch {
	case  == Second &&  > 19:
		return 0,  != "", xerrors.New("provided more than second precision for timestamp[s]")
	case  == Millisecond &&  > 23:
		return 0,  != "", xerrors.New("provided more than millisecond precision for timestamp[ms]")
	case  == Microsecond &&  > 26:
		return 0,  != "", xerrors.New("provided more than microsecond precision for timestamp[us]")
	}

	 += 
	,  := time.Parse(, )
	if  != nil {
		return 0,  != "", fmt.Errorf("%w: %s", ErrInvalid, )
	}
	if  != time.UTC {
		// convert to UTC by putting the same time instant in the desired location
		// before converting to UTC
		 = .In().UTC()
	}

	,  := TimestampFromTime(, )
	return ,  != "", 
}

// TimestampFromString parses a string and returns a timestamp for the given unit
// level.
//
// The timestamp should be in one of the following forms, [T] can be either T
// or a space, and [.zzzzzzzzz] can be either left out or up to 9 digits of
// fractions of a second.
//
//	YYYY-MM-DD
//	YYYY-MM-DD[T]HH
//	YYYY-MM-DD[T]HH:MM
//	YYYY-MM-DD[T]HH:MM:SS[.zzzzzzzz]
//
// You can also optionally have an ending Z to indicate UTC or indicate a specific
// timezone using ±HH, ±HHMM or ±HH:MM at the end of the string.
func ( string,  TimeUnit) (Timestamp, error) {
	, ,  := TimestampFromStringInLocation(, , time.UTC)
	return , 
}

func ( Timestamp) ( TimeUnit) time.Time {
	switch  {
	case Second:
		return time.Unix(int64(), 0).UTC()
	case Millisecond:
		return time.UnixMilli(int64()).UTC()
	case Microsecond:
		return time.UnixMicro(int64()).UTC()
	default:
		return time.Unix(0, int64()).UTC()
	}
}

// TimestampFromTime allows converting time.Time to Timestamp
func ( time.Time,  TimeUnit) (Timestamp, error) {
	switch  {
	case Second:
		return Timestamp(.Unix()), nil
	case Millisecond:
		return Timestamp(.Unix()*1e3 + int64(.Nanosecond())/1e6), nil
	case Microsecond:
		return Timestamp(.Unix()*1e6 + int64(.Nanosecond())/1e3), nil
	case Nanosecond:
		return Timestamp(.UnixNano()), nil
	default:
		return 0, fmt.Errorf("%w: unexpected timestamp unit: %s", ErrInvalid, )
	}
}

// Time32FromString parses a string to return a Time32 value in the given unit,
// unit needs to be only seconds or milliseconds and the string should be in the
// form of HH:MM or HH:MM:SS[.zzz] where the fractions of a second are optional.
func ( string,  TimeUnit) (Time32, error) {
	switch  {
	case Second:
		if len() > 8 {
			return 0, xerrors.New("cannot convert larger than second precision to time32s")
		}
	case Millisecond:
		if len() > 12 {
			return 0, xerrors.New("cannot convert larger than millisecond precision to time32ms")
		}
	case Microsecond, Nanosecond:
		return 0, xerrors.New("time32 can only be seconds or milliseconds")
	}

	var (
		 time.Time
		 error
	)
	switch {
	case len() == 5:
		,  = time.Parse("15:04", )
	default:
		,  = time.Parse("15:04:05.999", )
	}
	if  != nil {
		return 0, 
	}
	 := .Sub(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC))
	if  == Second {
		return Time32(.Seconds()), nil
	}
	return Time32(.Milliseconds()), nil
}

func ( Time32) ( TimeUnit) time.Time {
	return time.Unix(0, int64()*int64(.Multiplier())).UTC()
}

func ( Time32) ( TimeUnit) string {
	const  = "15:04:05"
	 := .ToTime()
	switch  {
	case Second:
		return .Format()
	case Millisecond:
		return .Format( + ".000")
	}
	return ""
}

// Time64FromString parses a string to return a Time64 value in the given unit,
// unit needs to be only microseconds or nanoseconds and the string should be in the
// form of HH:MM or HH:MM:SS[.zzzzzzzzz] where the fractions of a second are optional.
func ( string,  TimeUnit) (Time64, error) {
	// don't need to check length for nanoseconds as Parse will already error
	// if more than 9 digits are provided for the fractional second
	switch  {
	case Microsecond:
		if len() > 15 {
			return 0, xerrors.New("cannot convert larger than microsecond precision to time64us")
		}
	case Second, Millisecond:
		return 0, xerrors.New("time64 should only be microseconds or nanoseconds")
	}

	var (
		 time.Time
		 error
	)
	switch {
	case len() == 5:
		,  = time.Parse("15:04", )
	default:
		,  = time.Parse("15:04:05.999999999", )
	}
	if  != nil {
		return 0, 
	}
	 := .Sub(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC))
	if  == Microsecond {
		return Time64(.Microseconds()), nil
	}
	return Time64(.Nanoseconds()), nil
}

func ( Time64) ( TimeUnit) time.Time {
	return time.Unix(0, int64()*int64(.Multiplier())).UTC()
}

func ( Time64) ( TimeUnit) string {
	const  = "15:04:05.000000"
	 := .ToTime()
	switch  {
	case Microsecond:
		return .Format()
	case Nanosecond:
		return .Format( + "000")
	}
	return ""
}

const (
	Second TimeUnit = iota
	Millisecond
	Microsecond
	Nanosecond
)

var TimeUnitValues = []TimeUnit{Second, Millisecond, Microsecond, Nanosecond}

// Multiplier returns a time.Duration value to multiply by in order to
// convert the value into nanoseconds
func ( TimeUnit) () time.Duration {
	return [...]time.Duration{time.Second, time.Millisecond, time.Microsecond, time.Nanosecond}[uint()&3]
}

func ( TimeUnit) () string { return [...]string{"s", "ms", "us", "ns"}[uint()&3] }

type TemporalWithUnit interface {
	FixedWidthDataType
	TimeUnit() TimeUnit
}

// TimestampType is encoded as a 64-bit signed integer since the UNIX epoch (2017-01-01T00:00:00Z).
// The zero-value is a second and time zone neutral. In Arrow semantics, time zone neutral does not
// represent a physical point in time, but rather a "wall clock" time that only has meaning within
// the context that produced it. In Go, time.Time can only represent instants; there is no notion
// of "wall clock" time. Therefore, time zone neutral timestamps are represented as UTC per Go
// conventions even though the Arrow type itself has no time zone.
type TimestampType struct {
	Unit     TimeUnit
	TimeZone string

	loc *time.Location
	mx  sync.RWMutex
}

func (*TimestampType) () Type     { return TIMESTAMP }
func (*TimestampType) () string { return "timestamp" }
func ( *TimestampType) () string {
	switch len(.TimeZone) {
	case 0:
		return "timestamp[" + .Unit.String() + "]"
	default:
		return "timestamp[" + .Unit.String() + ", tz=" + .TimeZone + "]"
	}
}

func ( *TimestampType) () string {
	return fmt.Sprintf("%s%d:%s", typeFingerprint()+string(timeUnitFingerprint(.Unit)), len(.TimeZone), .TimeZone)
}

// BitWidth returns the number of bits required to store a single element of this data type in memory.
func (*TimestampType) () int { return 64 }

func (*TimestampType) () int { return Int64SizeBytes }

func (*TimestampType) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(TimestampSizeBytes)}}
}

func ( *TimestampType) () TimeUnit { return .Unit }

// ClearCachedLocation clears the cached time.Location object in the type.
// This should be called if you change the value of the TimeZone after having
// potentially called GetZone.
func ( *TimestampType) () {
	.mx.Lock()
	defer .mx.Unlock()
	.loc = nil
}

// GetZone returns a *time.Location that represents the current TimeZone member
// of the TimestampType. If it is "", "UTC", or "utc", you'll get time.UTC.
// Otherwise it must either be a valid tzdata string such as "America/New_York"
// or of the format +HH:MM or -HH:MM indicating an absolute offset.
//
// The location object will be cached in the TimestampType for subsequent calls
// so if you change the value of TimeZone after calling this, make sure to call
// ClearCachedLocation.
func ( *TimestampType) () (*time.Location, error) {
	.mx.RLock()
	if .loc != nil {
		defer .mx.RUnlock()
		return .loc, nil
	}

	.mx.RUnlock()
	.mx.Lock()
	defer .mx.Unlock()
	// in case GetZone() was called in between releasing the read lock and
	// getting the write lock
	if .loc != nil {
		return .loc, nil
	}
	// the TimeZone string is allowed to be either a valid tzdata string
	// such as "America/New_York" or an absolute offset of the form -XX:XX
	// or +XX:XX
	//
	// As such we have two methods we can try, first we'll try LoadLocation
	// and if that fails, we'll test for an absolute offset.
	if .TimeZone == "" || .TimeZone == "UTC" || .TimeZone == "utc" {
		.loc = time.UTC
		return time.UTC, nil
	}

	if ,  := time.LoadLocation(.TimeZone);  == nil {
		.loc = 
		return , 
	}

	// at this point we know that the timezone isn't empty, and didn't match
	// anything in the tzdata names. So either it's an absolute offset
	// or it's invalid.
	,  := time.Parse("-07:00", .TimeZone)
	if  != nil {
		return time.UTC, fmt.Errorf("could not find timezone location for '%s'", .TimeZone)
	}

	,  := .Zone()
	.loc = time.FixedZone(.TimeZone, )
	return .loc, nil
}

// GetToTimeFunc returns a function for converting an arrow.Timestamp value into a
// time.Time object with proper TimeZone and precision. If the TimeZone is invalid
// this will return an error. It calls GetZone to get the timezone for consistency.
func ( *TimestampType) () (func(Timestamp) time.Time, error) {
	,  := .GetZone()
	if  != nil {
		return nil, 
	}

	return func( Timestamp) time.Time { return .ToTime(.Unit).In() }, nil
}

// Time32Type is encoded as a 32-bit signed integer, representing either seconds or milliseconds since midnight.
type Time32Type struct {
	Unit TimeUnit
}

func (*Time32Type) () Type         { return TIME32 }
func (*Time32Type) () string     { return "time32" }
func (*Time32Type) () int    { return 32 }
func (*Time32Type) () int       { return Int32SizeBytes }
func ( *Time32Type) () string { return "time32[" + .Unit.String() + "]" }
func ( *Time32Type) () string {
	return typeFingerprint() + string(timeUnitFingerprint(.Unit))
}

func (Time32Type) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(Time32SizeBytes)}}
}

func ( *Time32Type) () TimeUnit { return .Unit }

// Time64Type is encoded as a 64-bit signed integer, representing either microseconds or nanoseconds since midnight.
type Time64Type struct {
	Unit TimeUnit
}

func (*Time64Type) () Type         { return TIME64 }
func (*Time64Type) () string     { return "time64" }
func (*Time64Type) () int    { return 64 }
func (*Time64Type) () int       { return Int64SizeBytes }
func ( *Time64Type) () string { return "time64[" + .Unit.String() + "]" }
func ( *Time64Type) () string {
	return typeFingerprint() + string(timeUnitFingerprint(.Unit))
}

func (Time64Type) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(Time64SizeBytes)}}
}

func ( *Time64Type) () TimeUnit { return .Unit }

// DurationType is encoded as a 64-bit signed integer, representing an amount
// of elapsed time without any relation to a calendar artifact.
type DurationType struct {
	Unit TimeUnit
}

func (*DurationType) () Type         { return DURATION }
func (*DurationType) () string     { return "duration" }
func (*DurationType) () int    { return 64 }
func (*DurationType) () int       { return Int64SizeBytes }
func ( *DurationType) () string { return "duration[" + .Unit.String() + "]" }
func ( *DurationType) () string {
	return typeFingerprint() + string(timeUnitFingerprint(.Unit))
}

func (DurationType) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(DurationSizeBytes)}}
}

func ( *DurationType) () TimeUnit { return .Unit }

// Float16Type represents a floating point value encoded with a 16-bit precision.
type Float16Type struct{}

func ( *Float16Type) () Type            { return FLOAT16 }
func ( *Float16Type) () string        { return "float16" }
func ( *Float16Type) () string      { return "float16" }
func ( *Float16Type) () string { return typeFingerprint() }

// BitWidth returns the number of bits required to store a single element of this data type in memory.
func ( *Float16Type) () int { return 16 }

func (Float16Type) () int { return Float16SizeBytes }

func (Float16Type) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(Float16SizeBytes)}}
}

type DecimalType interface {
	DataType
	GetPrecision() int32
	GetScale() int32
	BitWidth() int
}

// NarrowestDecimalType constructs the smallest decimal type that can represent
// the requested precision. An error is returned if the requested precision
// cannot be represented (prec <= 0 || prec > 76).
//
// For reference:
//
//	prec in [ 1,  9] => Decimal32Type
//	prec in [10, 18] => Decimal64Type
//	prec in [19, 38] => Decimal128Type
//	prec in [39, 76] => Decimal256Type
func (,  int32) (DecimalType, error) {
	switch {
	case  <= 0:
		return nil, fmt.Errorf("%w: precision must be > 0 for decimal types, got %d",
			ErrInvalid, )
	case  <= int32(decimal.MaxPrecision[decimal.Decimal32]()):
		return &Decimal32Type{Precision: , Scale: }, nil
	case  <= int32(decimal.MaxPrecision[decimal.Decimal64]()):
		return &Decimal64Type{Precision: , Scale: }, nil
	case  <= int32(decimal.MaxPrecision[decimal.Decimal128]()):
		return &Decimal128Type{Precision: , Scale: }, nil
	case  <= int32(decimal.MaxPrecision[decimal.Decimal256]()):
		return &Decimal256Type{Precision: , Scale: }, nil
	default:
		return nil, fmt.Errorf("%w: invalid precision for decimal types, %d",
			ErrInvalid, )
	}
}

func ( Type, ,  int32) (DecimalType, error) {
	switch  {
	case DECIMAL32:
		debug.Assert( <= int32(decimal.MaxPrecision[decimal.Decimal32]()), "invalid precision for decimal32")
		return &Decimal32Type{Precision: , Scale: }, nil
	case DECIMAL64:
		debug.Assert( <= int32(decimal.MaxPrecision[decimal.Decimal64]()), "invalid precision for decimal64")
		return &Decimal64Type{Precision: , Scale: }, nil
	case DECIMAL128:
		debug.Assert( <= int32(decimal.MaxPrecision[decimal.Decimal128]()), "invalid precision for decimal128")
		return &Decimal128Type{Precision: , Scale: }, nil
	case DECIMAL256:
		debug.Assert( <= int32(decimal.MaxPrecision[decimal.Decimal256]()), "invalid precision for decimal256")
		return &Decimal256Type{Precision: , Scale: }, nil
	default:
		return nil, fmt.Errorf("%w: must use one of the DECIMAL IDs to create a DecimalType", ErrInvalid)
	}
}

// Decimal32Type represents a fixed-size 32-bit decimal type.
type Decimal32Type struct {
	Precision int32
	Scale     int32
}

func (*Decimal32Type) () Type      { return DECIMAL32 }
func (*Decimal32Type) () string  { return "decimal32" }
func (*Decimal32Type) () int { return 32 }
func (*Decimal32Type) () int    { return Decimal32SizeBytes }
func ( *Decimal32Type) () string {
	return fmt.Sprintf("%s(%d, %d)", .Name(), .Precision, .Scale)
}
func ( *Decimal32Type) () string {
	return fmt.Sprintf("%s[%d,%d,%d]", typeFingerprint(), .BitWidth(), .Precision, .Scale)
}
func ( *Decimal32Type) () int32 { return .Precision }
func ( *Decimal32Type) () int32     { return .Scale }

func (Decimal32Type) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(Decimal32SizeBytes)}}
}

// Decimal64Type represents a fixed-size 32-bit decimal type.
type Decimal64Type struct {
	Precision int32
	Scale     int32
}

func (*Decimal64Type) () Type      { return DECIMAL64 }
func (*Decimal64Type) () string  { return "decimal64" }
func (*Decimal64Type) () int { return 64 }
func (*Decimal64Type) () int    { return Decimal64SizeBytes }
func ( *Decimal64Type) () string {
	return fmt.Sprintf("%s(%d, %d)", .Name(), .Precision, .Scale)
}
func ( *Decimal64Type) () string {
	return fmt.Sprintf("%s[%d,%d,%d]", typeFingerprint(), .BitWidth(), .Precision, .Scale)
}
func ( *Decimal64Type) () int32 { return .Precision }
func ( *Decimal64Type) () int32     { return .Scale }

func (Decimal64Type) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(Decimal64SizeBytes)}}
}

// Decimal128Type represents a fixed-size 128-bit decimal type.
type Decimal128Type struct {
	Precision int32
	Scale     int32
}

func (*Decimal128Type) () Type      { return DECIMAL128 }
func (*Decimal128Type) () string  { return "decimal" }
func (*Decimal128Type) () int { return 128 }
func (*Decimal128Type) () int    { return Decimal128SizeBytes }
func ( *Decimal128Type) () string {
	return fmt.Sprintf("%s(%d, %d)", .Name(), .Precision, .Scale)
}
func ( *Decimal128Type) () string {
	return fmt.Sprintf("%s[%d,%d,%d]", typeFingerprint(), .BitWidth(), .Precision, .Scale)
}
func ( *Decimal128Type) () int32 { return .Precision }
func ( *Decimal128Type) () int32     { return .Scale }

func (Decimal128Type) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(Decimal128SizeBytes)}}
}

// Decimal256Type represents a fixed-size 256-bit decimal type.
type Decimal256Type struct {
	Precision int32
	Scale     int32
}

func (*Decimal256Type) () Type      { return DECIMAL256 }
func (*Decimal256Type) () string  { return "decimal256" }
func (*Decimal256Type) () int { return 256 }
func (*Decimal256Type) () int    { return Decimal256SizeBytes }
func ( *Decimal256Type) () string {
	return fmt.Sprintf("%s(%d, %d)", .Name(), .Precision, .Scale)
}
func ( *Decimal256Type) () string {
	return fmt.Sprintf("%s[%d,%d,%d]", typeFingerprint(), .BitWidth(), .Precision, .Scale)
}
func ( *Decimal256Type) () int32 { return .Precision }
func ( *Decimal256Type) () int32     { return .Scale }

func (Decimal256Type) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(Decimal256SizeBytes)}}
}

// MonthInterval represents a number of months.
type MonthInterval int32

func ( *MonthInterval) ( []byte) error {
	var  struct {
		 int32 `json:"months"`
	}
	if  := json.Unmarshal(, &);  != nil {
		return 
	}

	* = MonthInterval(.)
	return nil
}

func ( MonthInterval) () ([]byte, error) {
	return json.Marshal(struct {
		 int32 `json:"months"`
	}{int32()})
}

// MonthIntervalType is encoded as a 32-bit signed integer,
// representing a number of months.
type MonthIntervalType struct{}

func (*MonthIntervalType) () Type            { return INTERVAL_MONTHS }
func (*MonthIntervalType) () string        { return "month_interval" }
func (*MonthIntervalType) () string      { return "month_interval" }
func (*MonthIntervalType) () string { return typeIDFingerprint(INTERVAL_MONTHS) + "M" }

// BitWidth returns the number of bits required to store a single element of this data type in memory.
func ( *MonthIntervalType) () int { return 32 }

func (MonthIntervalType) () int { return Int32SizeBytes }
func (MonthIntervalType) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(MonthIntervalSizeBytes)}}
}

// DayTimeInterval represents a number of days and milliseconds (fraction of day).
type DayTimeInterval struct {
	Days         int32 `json:"days"`
	Milliseconds int32 `json:"milliseconds"`
}

// DayTimeIntervalType is encoded as a pair of 32-bit signed integer,
// representing a number of days and milliseconds (fraction of day).
type DayTimeIntervalType struct{}

func (*DayTimeIntervalType) () Type            { return INTERVAL_DAY_TIME }
func (*DayTimeIntervalType) () string        { return "day_time_interval" }
func (*DayTimeIntervalType) () string      { return "day_time_interval" }
func (*DayTimeIntervalType) () string { return typeIDFingerprint(INTERVAL_DAY_TIME) + "d" }

// BitWidth returns the number of bits required to store a single element of this data type in memory.
func ( *DayTimeIntervalType) () int { return 64 }

func (DayTimeIntervalType) () int { return DayTimeIntervalSizeBytes }
func (DayTimeIntervalType) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(DayTimeIntervalSizeBytes)}}
}

// MonthDayNanoInterval represents a number of months, days and nanoseconds (fraction of day).
type MonthDayNanoInterval struct {
	Months      int32 `json:"months"`
	Days        int32 `json:"days"`
	Nanoseconds int64 `json:"nanoseconds"`
}

// MonthDayNanoIntervalType is encoded as two signed 32-bit integers representing
// a number of months and a number of days, followed by a 64-bit integer representing
// the number of nanoseconds since midnight for fractions of a day.
type MonthDayNanoIntervalType struct{}

func (*MonthDayNanoIntervalType) () Type       { return INTERVAL_MONTH_DAY_NANO }
func (*MonthDayNanoIntervalType) () string   { return "month_day_nano_interval" }
func (*MonthDayNanoIntervalType) () string { return "month_day_nano_interval" }
func (*MonthDayNanoIntervalType) () string {
	return typeIDFingerprint(INTERVAL_MONTH_DAY_NANO) + "N"
}

// BitWidth returns the number of bits required to store a single element of this data type in memory.
func (*MonthDayNanoIntervalType) () int { return 128 }
func (*MonthDayNanoIntervalType) () int    { return MonthDayNanoIntervalSizeBytes }
func (MonthDayNanoIntervalType) () DataTypeLayout {
	return DataTypeLayout{Buffers: []BufferSpec{SpecBitmap(), SpecFixedWidth(MonthDayNanoIntervalSizeBytes)}}
}

type TimestampConvertOp int8

const (
	ConvDIVIDE = iota
	ConvMULTIPLY
)

var timestampConversion = [...][4]struct {
	op     TimestampConvertOp
	factor int64
}{
	Nanosecond: {
		Nanosecond:  {ConvMULTIPLY, int64(time.Nanosecond)},
		Microsecond: {ConvDIVIDE, int64(time.Microsecond)},
		Millisecond: {ConvDIVIDE, int64(time.Millisecond)},
		Second:      {ConvDIVIDE, int64(time.Second)},
	},
	Microsecond: {
		Nanosecond:  {ConvMULTIPLY, int64(time.Microsecond)},
		Microsecond: {ConvMULTIPLY, 1},
		Millisecond: {ConvDIVIDE, int64(time.Millisecond / time.Microsecond)},
		Second:      {ConvDIVIDE, int64(time.Second / time.Microsecond)},
	},
	Millisecond: {
		Nanosecond:  {ConvMULTIPLY, int64(time.Millisecond)},
		Microsecond: {ConvMULTIPLY, int64(time.Millisecond / time.Microsecond)},
		Millisecond: {ConvMULTIPLY, 1},
		Second:      {ConvDIVIDE, int64(time.Second / time.Millisecond)},
	},
	Second: {
		Nanosecond:  {ConvMULTIPLY, int64(time.Second)},
		Microsecond: {ConvMULTIPLY, int64(time.Second / time.Microsecond)},
		Millisecond: {ConvMULTIPLY, int64(time.Second / time.Millisecond)},
		Second:      {ConvMULTIPLY, 1},
	},
}

func (,  TimeUnit) ( TimestampConvertOp,  int64) {
	 := timestampConversion[int()][int()]
	return .op, .factor
}

func (,  TimeUnit,  int64) int64 {
	 := timestampConversion[int()][int()]
	switch .op {
	case ConvMULTIPLY:
		return  * .factor
	case ConvDIVIDE:
		return  / .factor
	}

	return 0
}

// DictionaryType represents categorical or dictionary-encoded in-memory data
// It contains a dictionary-encoded value type (any type) and an index type
// (any integer type).
type DictionaryType struct {
	IndexType DataType
	ValueType DataType
	Ordered   bool
}

func (*DictionaryType) () Type        { return DICTIONARY }
func (*DictionaryType) () string    { return "dictionary" }
func ( *DictionaryType) () int { return .IndexType.(FixedWidthDataType).BitWidth() }
func ( *DictionaryType) () int    { return .IndexType.(FixedWidthDataType).Bytes() }
func ( *DictionaryType) () string {
	return fmt.Sprintf("%s<values=%s, indices=%s, ordered=%t>",
		.Name(), .ValueType, .IndexType, .Ordered)
}
func ( *DictionaryType) () string {
	 := .IndexType.Fingerprint()
	 := .ValueType.Fingerprint()
	 := "1"
	if !.Ordered {
		 = "0"
	}

	if len() > 0 {
		return typeFingerprint() +  +  + 
	}
	return 
}

func ( *DictionaryType) () DataTypeLayout {
	 := .IndexType.Layout()
	.HasDict = true
	return 
}

var (
	FixedWidthTypes = struct {
		Boolean              FixedWidthDataType
		Date32               FixedWidthDataType
		Date64               FixedWidthDataType
		DayTimeInterval      FixedWidthDataType
		Duration_s           FixedWidthDataType
		Duration_ms          FixedWidthDataType
		Duration_us          FixedWidthDataType
		Duration_ns          FixedWidthDataType
		Float16              FixedWidthDataType
		MonthInterval        FixedWidthDataType
		Time32s              FixedWidthDataType
		Time32ms             FixedWidthDataType
		Time64us             FixedWidthDataType
		Time64ns             FixedWidthDataType
		Timestamp_s          FixedWidthDataType
		Timestamp_ms         FixedWidthDataType
		Timestamp_us         FixedWidthDataType
		Timestamp_ns         FixedWidthDataType
		MonthDayNanoInterval FixedWidthDataType
	}{
		Boolean:              &BooleanType{},
		Date32:               &Date32Type{},
		Date64:               &Date64Type{},
		DayTimeInterval:      &DayTimeIntervalType{},
		Duration_s:           &DurationType{Unit: Second},
		Duration_ms:          &DurationType{Unit: Millisecond},
		Duration_us:          &DurationType{Unit: Microsecond},
		Duration_ns:          &DurationType{Unit: Nanosecond},
		Float16:              &Float16Type{},
		MonthInterval:        &MonthIntervalType{},
		Time32s:              &Time32Type{Unit: Second},
		Time32ms:             &Time32Type{Unit: Millisecond},
		Time64us:             &Time64Type{Unit: Microsecond},
		Time64ns:             &Time64Type{Unit: Nanosecond},
		Timestamp_s:          &TimestampType{Unit: Second, TimeZone: "UTC"},
		Timestamp_ms:         &TimestampType{Unit: Millisecond, TimeZone: "UTC"},
		Timestamp_us:         &TimestampType{Unit: Microsecond, TimeZone: "UTC"},
		Timestamp_ns:         &TimestampType{Unit: Nanosecond, TimeZone: "UTC"},
		MonthDayNanoInterval: &MonthDayNanoIntervalType{},
	}

	_ FixedWidthDataType = (*FixedSizeBinaryType)(nil)
)