// 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 schema

import (
	
	

	
	
	
	format 
	
)

// DecimalMetadata is a struct for managing scale and precision information between
// converted and logical types.
type DecimalMetadata struct {
	IsSet     bool
	Scale     int32
	Precision int32
}

func getLogicalType( *format.LogicalType) LogicalType {
	switch {
	case .IsSetSTRING():
		return StringLogicalType{}
	case .IsSetMAP():
		return MapLogicalType{}
	case .IsSetLIST():
		return ListLogicalType{}
	case .IsSetENUM():
		return EnumLogicalType{}
	case .IsSetDECIMAL():
		return DecimalLogicalType{typ: .DECIMAL}
	case .IsSetDATE():
		return DateLogicalType{}
	case .IsSetTIME():
		if timeUnitFromThrift(.TIME.Unit) == TimeUnitUnknown {
			panic("parquet: TimeUnit must be one of MILLIS, MICROS, or NANOS for Time logical type")
		}
		return TimeLogicalType{typ: .TIME}
	case .IsSetTIMESTAMP():
		if timeUnitFromThrift(.TIMESTAMP.Unit) == TimeUnitUnknown {
			panic("parquet: TimeUnit must be one of MILLIS, MICROS, or NANOS for Timestamp logical type")
		}
		return TimestampLogicalType{typ: .TIMESTAMP}
	case .IsSetINTEGER():
		return IntLogicalType{typ: .INTEGER}
	case .IsSetUNKNOWN():
		return NullLogicalType{}
	case .IsSetJSON():
		return JSONLogicalType{}
	case .IsSetBSON():
		return BSONLogicalType{}
	case .IsSetUUID():
		return UUIDLogicalType{}
	case .IsSetFLOAT16():
		return Float16LogicalType{}
	case .IsSetVARIANT():
		return VariantLogicalType{}
	case  == nil:
		return NoLogicalType{}
	default:
		panic("invalid logical type")
	}
}

// TimeUnitType is an enum for denoting whether a time based logical type
// is using milliseconds, microseconds or nanoseconds.
type TimeUnitType int

// Constants for the TimeUnitType
const (
	TimeUnitMillis TimeUnitType = iota
	TimeUnitMicros
	TimeUnitNanos
	TimeUnitUnknown
)

// LogicalType is the descriptor that defines the usage of a physical primitive
// type in the schema, such as an Interval, Date, etc.
type LogicalType interface {
	// Returns true if a nested type like List or Map
	IsNested() bool
	// Returns true if this type can be serialized, ie: not Unknown/NoType/Interval
	IsSerialized() bool
	// Returns true if not NoLogicalType
	IsValid() bool
	// Returns true if it is NoType
	IsNone() bool
	// returns a string representation of the Logical Type
	String() string
	toThrift() *format.LogicalType
	// Return the equivalent ConvertedType for legacy Parquet systems
	ToConvertedType() (ConvertedType, DecimalMetadata)
	// Returns true if the specified ConvertedType is compatible with this
	// logical type
	IsCompatible(ConvertedType, DecimalMetadata) bool
	// Returns true if this logical type can be used with the provided physical type
	IsApplicable(t parquet.Type, tlen int32) bool
	// Returns true if the logical types are the same
	Equals(LogicalType) bool
	// Returns the default stat sort order for this logical type
	SortOrder() SortOrder
}

// TemporalLogicalType is a smaller interface for Time based logical types
// like Time / Timestamp
type TemporalLogicalType interface {
	LogicalType
	IsAdjustedToUTC() bool
	TimeUnit() TimeUnitType
}

// SortOrder mirrors the parquet.thrift sort order type
type SortOrder int8

// Constants for the Stat sort order definitions
const (
	SortSIGNED SortOrder = iota
	SortUNSIGNED
	SortUNKNOWN
)

// DefaultSortOrder returns the default stat sort order for the given physical type
func ( format.Type) SortOrder {
	switch  {
	case format.Type_BOOLEAN, format.Type_INT32, format.Type_INT64, format.Type_FLOAT, format.Type_DOUBLE:
		return SortSIGNED
	case format.Type_BYTE_ARRAY, format.Type_FIXED_LEN_BYTE_ARRAY:
		return SortUNSIGNED
	case format.Type_INT96:
		fallthrough
	default:
		return SortUNKNOWN
	}
}

// GetLogicalSortOrder returns the default sort order for this logical type
// or falls back to the default sort order for the physical type if not valid
func ( LogicalType,  format.Type) SortOrder {
	switch {
	case  == nil || !.IsValid():
		return SortUNKNOWN
	case .Equals(NoLogicalType{}):
		return DefaultSortOrder()
	default:
		return .SortOrder()
	}
}

type baseLogicalType struct{}

func (baseLogicalType) () bool {
	return true
}

func (baseLogicalType) () bool {
	return true
}

func (baseLogicalType) () bool {
	return false
}

func (baseLogicalType) () bool { return false }

// StringLogicalType is a UTF8 string, only usable with ByteArray and FixedLenByteArray
type StringLogicalType struct{ baseLogicalType }

func (StringLogicalType) () SortOrder {
	return SortUNSIGNED
}

func (StringLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": StringLogicalType{}.String()})
}

func (StringLogicalType) () string {
	return "String"
}

func (StringLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.UTF8, DecimalMetadata{}
}

func (StringLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.UTF8 && !.IsSet
}

func (StringLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.ByteArray
}

func (StringLogicalType) () *format.LogicalType {
	return &format.LogicalType{STRING: format.NewStringType()}
}

func (StringLogicalType) ( LogicalType) bool {
	,  := .(StringLogicalType)
	return 
}

// MapLogicalType represents a mapped type
type MapLogicalType struct{ baseLogicalType }

func (MapLogicalType) () SortOrder {
	return SortUNKNOWN
}

func (MapLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": MapLogicalType{}.String()})
}

func (MapLogicalType) () string {
	return "Map"
}

func (MapLogicalType) () bool {
	return true
}

func (MapLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.Map, DecimalMetadata{}
}

func (MapLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return ( == ConvertedTypes.Map ||  == ConvertedTypes.MapKeyValue) && !.IsSet
}

func (MapLogicalType) (parquet.Type, int32) bool {
	return false
}

func (MapLogicalType) () *format.LogicalType {
	return &format.LogicalType{MAP: format.NewMapType()}
}

func (MapLogicalType) ( LogicalType) bool {
	,  := .(MapLogicalType)
	return 
}

func () LogicalType {
	return ListLogicalType{}
}

// ListLogicalType is used for columns which are themselves nested lists
type ListLogicalType struct{ baseLogicalType }

func (ListLogicalType) () SortOrder {
	return SortUNKNOWN
}

func (ListLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": ListLogicalType{}.String()})
}

func (ListLogicalType) () string {
	return "List"
}

func (ListLogicalType) () bool {
	return true
}

func (ListLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.List, DecimalMetadata{}
}

func (ListLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.List && !.IsSet
}

func (ListLogicalType) (parquet.Type, int32) bool {
	return false
}

func (ListLogicalType) () *format.LogicalType {
	return &format.LogicalType{LIST: format.NewListType()}
}

func (ListLogicalType) ( LogicalType) bool {
	,  := .(ListLogicalType)
	return 
}

// EnumLogicalType is for representing an enum, which should be a byte array type
type EnumLogicalType struct{ baseLogicalType }

func (EnumLogicalType) () SortOrder {
	return SortUNSIGNED
}

func (EnumLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": EnumLogicalType{}.String()})
}

func (EnumLogicalType) () string {
	return "Enum"
}

func (EnumLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.Enum, DecimalMetadata{}
}

func (EnumLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.Enum && !.IsSet
}

func (EnumLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.ByteArray
}

func (EnumLogicalType) () *format.LogicalType {
	return &format.LogicalType{ENUM: format.NewEnumType()}
}

func (EnumLogicalType) ( LogicalType) bool {
	,  := .(EnumLogicalType)
	return 
}

// NewDecimalLogicalType returns a Decimal logical type with the given
// precision and scale.
//
// Panics if precision < 1 or scale is not in the range (0, precision)
func ( int32,  int32) LogicalType {
	if  < 1 {
		panic("parquet: precision must be greater than or equal to 1 for decimal logical type")
	}
	if  < 0 ||  >  {
		panic("parquet: scale must be a non-negative integer that does not exceed precision for decimal logical type")
	}
	return DecimalLogicalType{typ: &format.DecimalType{Precision: , Scale: }}
}

// DecimalLogicalType is used to represent a decimal value of a given
// precision and scale
type DecimalLogicalType struct {
	baseLogicalType
	typ *format.DecimalType
}

func ( DecimalLogicalType) () int32 {
	return .typ.Precision
}

func ( DecimalLogicalType) () int32 {
	return .typ.Scale
}

func (DecimalLogicalType) () SortOrder {
	return SortSIGNED
}

func ( DecimalLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]interface{}{"Type": "Decimal", "precision": .typ.Precision, "scale": .typ.Scale})
}

func ( DecimalLogicalType) () string {
	return fmt.Sprintf("Decimal(precision=%d, scale=%d)", .typ.Precision, .typ.Scale)
}

func ( DecimalLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.Decimal, DecimalMetadata{IsSet: true, Scale: .typ.GetScale(), Precision: .typ.GetPrecision()}
}

func ( DecimalLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.Decimal &&
		.IsSet && .Scale == .typ.Scale && .Precision == .typ.Precision
}

func ( DecimalLogicalType) ( parquet.Type,  int32) bool {
	switch  {
	case parquet.Types.Int32:
		return 1 <= .typ.Precision && .typ.Precision <= 9
	case parquet.Types.Int64:
		if .typ.Precision < 10 {
			debug.Log("int64 used for decimal logical, precision is small enough to use int32")
		}
		return 1 <= .typ.Precision && .typ.Precision <= 18
	case parquet.Types.FixedLenByteArray:
		return .typ.Precision <= int32(math.Floor(math.Log10(math.Pow(2.0, (8.0*float64()-1.0)))))
	case parquet.Types.ByteArray:
		return true
	}
	return false
}

func ( DecimalLogicalType) () *format.LogicalType {
	return &format.LogicalType{DECIMAL: .typ}
}

func ( DecimalLogicalType) ( LogicalType) bool {
	,  := .(DecimalLogicalType)
	if ! {
		return false
	}
	return .typ.Precision == .typ.Precision && .typ.Scale == .typ.Scale
}

// DateLogicalType is an int32 representing the number of days since the Unix Epoch
// 1 January 1970
type DateLogicalType struct{ baseLogicalType }

func (DateLogicalType) () SortOrder {
	return SortSIGNED
}

func (DateLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": DateLogicalType{}.String()})
}

func (DateLogicalType) () string {
	return "Date"
}

func (DateLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.Date, DecimalMetadata{}
}

func (DateLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.Date && !.IsSet
}

func (DateLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.Int32
}

func (DateLogicalType) () *format.LogicalType {
	return &format.LogicalType{DATE: format.NewDateType()}
}

func (DateLogicalType) ( LogicalType) bool {
	,  := .(DateLogicalType)
	return 
}

func timeUnitFromThrift( *format.TimeUnit) TimeUnitType {
	switch {
	case  == nil:
		return TimeUnitUnknown
	case .IsSetMILLIS():
		return TimeUnitMillis
	case .IsSetMICROS():
		return TimeUnitMicros
	case .IsSetNANOS():
		return TimeUnitNanos
	default:
		return TimeUnitUnknown
	}
}

func timeUnitToString( *format.TimeUnit) string {
	switch {
	case  == nil:
		return "unknown"
	case .IsSetMILLIS():
		return "milliseconds"
	case .IsSetMICROS():
		return "microseconds"
	case .IsSetNANOS():
		return "nanoseconds"
	default:
		return "unknown"
	}
}

func timeUnitFromString( string) TimeUnitType {
	switch  {
	case "millis":
		return TimeUnitMillis
	case "micros":
		return TimeUnitMicros
	case "nanos":
		return TimeUnitNanos
	default:
		return TimeUnitUnknown
	}
}

func createTimeUnit( TimeUnitType) *format.TimeUnit {
	 := format.NewTimeUnit()
	switch  {
	case TimeUnitMicros:
		.MICROS = format.NewMicroSeconds()
	case TimeUnitMillis:
		.MILLIS = format.NewMilliSeconds()
	case TimeUnitNanos:
		.NANOS = format.NewNanoSeconds()
	default:
		panic("parquet: time unit must be one of MILLIS, MICROS, or NANOS for Time logical type")
	}
	return 
}

// NewTimeLogicalType returns a time type of the given unit.
func ( bool,  TimeUnitType) LogicalType {
	return TimeLogicalType{typ: &format.TimeType{
		IsAdjustedToUTC: ,
		Unit:            createTimeUnit(),
	}}
}

// TimeLogicalType is a time type without a date and must be an
// int32 for milliseconds, or an int64 for micro or nano seconds.
type TimeLogicalType struct {
	baseLogicalType
	typ *format.TimeType
}

func ( TimeLogicalType) () bool {
	return .typ.IsAdjustedToUTC
}

func ( TimeLogicalType) () TimeUnitType {
	return timeUnitFromThrift(.typ.Unit)
}

func (TimeLogicalType) () SortOrder {
	return SortSIGNED
}

func ( TimeLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"Type": "Time", "isAdjustedToUTC": .typ.IsAdjustedToUTC, "timeUnit": timeUnitToString(.typ.GetUnit())})
}

func ( TimeLogicalType) () string {
	return fmt.Sprintf("Time(isAdjustedToUTC=%t, timeUnit=%s)", .typ.GetIsAdjustedToUTC(), timeUnitToString(.typ.GetUnit()))
}

func ( TimeLogicalType) () (ConvertedType, DecimalMetadata) {
	 := timeUnitFromThrift(.typ.Unit)
	if .typ.IsAdjustedToUTC {
		switch  {
		case TimeUnitMillis:
			return ConvertedTypes.TimeMillis, DecimalMetadata{}
		case TimeUnitMicros:
			return ConvertedTypes.TimeMicros, DecimalMetadata{}
		}
	}
	return ConvertedTypes.None, DecimalMetadata{}
}

func ( TimeLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	if .IsSet {
		return false
	}
	 := timeUnitFromThrift(.typ.Unit)
	if .typ.IsAdjustedToUTC {
		switch  {
		case TimeUnitMillis:
			return  == ConvertedTypes.TimeMillis
		case TimeUnitMicros:
			return  == ConvertedTypes.TimeMicros
		}
	}

	return  == ConvertedTypes.None ||  == ConvertedTypes.NA
}

func ( TimeLogicalType) ( parquet.Type,  int32) bool {
	return ( == parquet.Types.Int32 && .typ.GetUnit().IsSetMILLIS()) ||
		( == parquet.Types.Int64 &&
			(.typ.GetUnit().IsSetMICROS() || .typ.GetUnit().IsSetNANOS()))
}

func ( TimeLogicalType) () *format.LogicalType {
	return &format.LogicalType{TIME: .typ}
}

func ( TimeLogicalType) ( LogicalType) bool {
	,  := .(TimeLogicalType)
	if ! {
		return false
	}
	return .typ.IsAdjustedToUTC == .typ.IsAdjustedToUTC &&
		timeUnitFromThrift(.typ.Unit) == timeUnitFromThrift(.typ.Unit)
}

// NewTimestampLogicalType returns a logical timestamp type with "forceConverted"
// set to false
func ( bool,  TimeUnitType) LogicalType {
	return TimestampLogicalType{
		typ: &format.TimestampType{
			IsAdjustedToUTC: ,
			Unit:            createTimeUnit(),
		},
		forceConverted: false,
		fromConverted:  false,
	}
}

// NewTimestampLogicalTypeForce returns a timestamp logical type with
// "forceConverted" set to true
func ( bool,  TimeUnitType) LogicalType {
	return TimestampLogicalType{
		typ: &format.TimestampType{
			IsAdjustedToUTC: ,
			Unit:            createTimeUnit(),
		},
		forceConverted: true,
		fromConverted:  false,
	}
}

// TimestampOpt options used with New Timestamp Logical Type
type TimestampOpt func(*TimestampLogicalType)

// WithTSIsAdjustedToUTC sets the IsAdjustedToUTC field of the timestamp type.
func () TimestampOpt {
	return func( *TimestampLogicalType) {
		.typ.IsAdjustedToUTC = true
	}
}

// WithTSTimeUnitType sets the time unit for the timestamp type
func ( TimeUnitType) TimestampOpt {
	return func( *TimestampLogicalType) {
		.typ.Unit = createTimeUnit()
	}
}

// WithTSForceConverted enable force converted mode
func () TimestampOpt {
	return func( *TimestampLogicalType) {
		.forceConverted = true
	}
}

// WithTSFromConverted enable the timestamp logical type to be
// constructed from a converted type.
func () TimestampOpt {
	return func( *TimestampLogicalType) {
		.fromConverted = true
	}
}

// NewTimestampLogicalTypeWithOpts creates a new TimestampLogicalType with the provided options.
//
// TimestampType Unit defaults to milliseconds (TimeUnitMillis)
func ( ...TimestampOpt) LogicalType {
	 := TimestampLogicalType{
		typ: &format.TimestampType{
			Unit: createTimeUnit(TimeUnitMillis), // default to milliseconds
		},
	}

	for ,  := range  {
		(&)
	}

	return 
}

// TimestampLogicalType represents an int64 number that can be decoded
// into a year, month, day, hour, minute, second, and subsecond
type TimestampLogicalType struct {
	baseLogicalType
	typ *format.TimestampType
	// forceConverted denotes whether or not the resulting serialized
	// type when writing to parquet will be written as the legacy
	// ConvertedType TIMESTAMP_MICROS/TIMESTAMP_MILLIS (true)
	// or if it will write the proper current Logical Types (false, default)
	forceConverted bool
	// fromConverted denotes if the timestamp type was created by
	// translating a legacy converted type of TIMESTAMP_MILLIS or
	// TIMESTAMP_MICROS rather than by using the current logical
	// types. Default is false.
	fromConverted bool
}

func ( TimestampLogicalType) () bool {
	return .fromConverted
}

func ( TimestampLogicalType) () bool {
	return .typ.IsAdjustedToUTC
}

func ( TimestampLogicalType) () TimeUnitType {
	return timeUnitFromThrift(.typ.Unit)
}

func (TimestampLogicalType) () SortOrder {
	return SortSIGNED
}

func ( TimestampLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"Type":                     "Timestamp",
		"isAdjustedToUTC":          .typ.IsAdjustedToUTC,
		"timeUnit":                 timeUnitToString(.typ.GetUnit()),
		"is_from_converted_type":   .fromConverted,
		"force_set_converted_type": .forceConverted,
	})
}

func ( TimestampLogicalType) () bool {
	return !.fromConverted
}

func ( TimestampLogicalType) () string {
	return fmt.Sprintf("Timestamp(isAdjustedToUTC=%t, timeUnit=%s, is_from_converted_type=%t, force_set_converted_type=%t)",
		.typ.GetIsAdjustedToUTC(), timeUnitToString(.typ.GetUnit()), .fromConverted, .forceConverted)
}

func ( TimestampLogicalType) () (ConvertedType, DecimalMetadata) {
	 := timeUnitFromThrift(.typ.Unit)
	if .typ.IsAdjustedToUTC || .forceConverted {
		switch  {
		case TimeUnitMillis:
			return ConvertedTypes.TimestampMillis, DecimalMetadata{}
		case TimeUnitMicros:
			return ConvertedTypes.TimestampMicros, DecimalMetadata{}
		}
	}
	return ConvertedTypes.None, DecimalMetadata{}
}

func ( TimestampLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	if .IsSet {
		return false
	}

	switch timeUnitFromThrift(.typ.Unit) {
	case TimeUnitMillis:
		if .typ.GetIsAdjustedToUTC() || .forceConverted {
			return  == ConvertedTypes.TimestampMillis
		}
	case TimeUnitMicros:
		if .typ.GetIsAdjustedToUTC() || .forceConverted {
			return  == ConvertedTypes.TimestampMicros
		}
	}

	return  == ConvertedTypes.None ||  == ConvertedTypes.NA
}

func (TimestampLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.Int64
}

func ( TimestampLogicalType) () *format.LogicalType {
	return &format.LogicalType{TIMESTAMP: .typ}
}

func ( TimestampLogicalType) ( LogicalType) bool {
	,  := .(TimestampLogicalType)
	if ! {
		return false
	}
	return .typ.IsAdjustedToUTC == .typ.IsAdjustedToUTC &&
		timeUnitFromThrift(.typ.Unit) == timeUnitFromThrift(.typ.Unit)
}

// NewIntLogicalType creates an integer logical type of the desired bitwidth
// and whether it is signed or not.
//
// Bit width must be exactly 8, 16, 32 or 64 for an integer logical type
func ( int8,  bool) LogicalType {
	switch  {
	case 8, 16, 32, 64:
	default:
		panic("parquet: bit width must be exactly 8, 16, 32, or 64 for Int logical type")
	}
	return IntLogicalType{
		typ: &format.IntType{
			BitWidth: ,
			IsSigned: ,
		},
	}
}

// IntLogicalType represents an integer type of a specific bit width and
// is either signed or unsigned.
type IntLogicalType struct {
	baseLogicalType
	typ *format.IntType
}

func ( IntLogicalType) () int8 {
	return .typ.BitWidth
}

func ( IntLogicalType) () bool {
	return .typ.IsSigned
}

func ( IntLogicalType) () SortOrder {
	if .typ.IsSigned {
		return SortSIGNED
	}
	return SortUNSIGNED
}

func ( IntLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"Type": "Int", "bitWidth": .typ.BitWidth, "isSigned": .typ.IsSigned,
	})
}

func ( IntLogicalType) () string {
	return fmt.Sprintf("Int(bitWidth=%d, isSigned=%t)", .typ.GetBitWidth(), .typ.GetIsSigned())
}

func ( IntLogicalType) () (ConvertedType, DecimalMetadata) {
	var  DecimalMetadata
	if .typ.IsSigned {
		switch .typ.BitWidth {
		case 8:
			return ConvertedTypes.Int8, 
		case 16:
			return ConvertedTypes.Int16, 
		case 32:
			return ConvertedTypes.Int32, 
		case 64:
			return ConvertedTypes.Int64, 
		}
	} else {
		switch .typ.BitWidth {
		case 8:
			return ConvertedTypes.Uint8, 
		case 16:
			return ConvertedTypes.Uint16, 
		case 32:
			return ConvertedTypes.Uint32, 
		case 64:
			return ConvertedTypes.Uint64, 
		}
	}
	return ConvertedTypes.None, 
}

func ( IntLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	if .IsSet {
		return false
	}
	,  := .ToConvertedType()
	return  == 
}

func ( IntLogicalType) ( parquet.Type,  int32) bool {
	return ( == parquet.Types.Int32 && .typ.GetBitWidth() <= 32) ||
		( == parquet.Types.Int64 && .typ.GetBitWidth() == 64)
}

func ( IntLogicalType) () *format.LogicalType {
	return &format.LogicalType{INTEGER: .typ}
}

func ( IntLogicalType) ( LogicalType) bool {
	,  := .(IntLogicalType)
	if ! {
		return false
	}

	return .typ.GetIsSigned() == .typ.GetIsSigned() &&
		.typ.GetBitWidth() == .typ.GetBitWidth()
}

// UnknownLogicalType is a type that is essentially a placeholder for when
// we don't know the type.
type UnknownLogicalType struct{ baseLogicalType }

func (UnknownLogicalType) () SortOrder {
	return SortUNKNOWN
}

func (UnknownLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": UnknownLogicalType{}.String()})
}

func (UnknownLogicalType) () bool { return false }

func (UnknownLogicalType) () bool { return false }

func (UnknownLogicalType) () string {
	return "Unknown"
}

func (UnknownLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.NA, DecimalMetadata{}
}

func (UnknownLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.NA && !.IsSet
}

func (UnknownLogicalType) (parquet.Type, int32) bool { return true }

func (UnknownLogicalType) () *format.LogicalType {
	return &format.LogicalType{UNKNOWN: format.NewNullType()}
}

func (UnknownLogicalType) ( LogicalType) bool {
	,  := .(UnknownLogicalType)
	return 
}

// JSONLogicalType represents a byte array column which is to be interpreted
// as a JSON string.
type JSONLogicalType struct{ baseLogicalType }

func (JSONLogicalType) () SortOrder {
	return SortUNSIGNED
}

func (JSONLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": JSONLogicalType{}.String()})
}

func (JSONLogicalType) () string {
	return "JSON"
}

func (JSONLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.JSON, DecimalMetadata{}
}

func (JSONLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.JSON && !.IsSet
}

func (JSONLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.ByteArray
}

func (JSONLogicalType) () *format.LogicalType {
	return &format.LogicalType{JSON: format.NewJsonType()}
}

func (JSONLogicalType) ( LogicalType) bool {
	,  := .(JSONLogicalType)
	return 
}

// BSONLogicalType represents a binary JSON string in the byte array
type BSONLogicalType struct{ baseLogicalType }

func (BSONLogicalType) () SortOrder {
	return SortUNSIGNED
}

func (BSONLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": BSONLogicalType{}.String()})
}

func (BSONLogicalType) () string {
	return "BSON"
}

func (BSONLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.BSON, DecimalMetadata{}
}

func (BSONLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.BSON && !.IsSet
}

func (BSONLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.ByteArray
}

func (BSONLogicalType) () *format.LogicalType {
	return &format.LogicalType{BSON: format.NewBsonType()}
}

func (BSONLogicalType) ( LogicalType) bool {
	,  := .(BSONLogicalType)
	return 
}

// UUIDLogicalType can only be used with a FixedLength byte array column
// that is exactly 16 bytes long
type UUIDLogicalType struct{ baseLogicalType }

func (UUIDLogicalType) () SortOrder {
	return SortUNSIGNED
}

func (UUIDLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": UUIDLogicalType{}.String()})
}

func (UUIDLogicalType) () string {
	return "UUID"
}

func (UUIDLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.None, DecimalMetadata{}
}

func (UUIDLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	if .IsSet {
		return false
	}
	switch  {
	case ConvertedTypes.None, ConvertedTypes.NA:
		return true
	}
	return false
}

func (UUIDLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.FixedLenByteArray &&  == 16
}

func (UUIDLogicalType) () *format.LogicalType {
	return &format.LogicalType{UUID: format.NewUUIDType()}
}

func (UUIDLogicalType) ( LogicalType) bool {
	,  := .(UUIDLogicalType)
	return 
}

// IntervalLogicalType is not yet in the thrift spec, but represents
// an interval time and needs to be a fixed length byte array of 12 bytes
type IntervalLogicalType struct{ baseLogicalType }

func (IntervalLogicalType) () SortOrder {
	return SortUNKNOWN
}

func (IntervalLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": IntervalLogicalType{}.String()})
}

func (IntervalLogicalType) () string {
	return "Interval"
}

func (IntervalLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.Interval, DecimalMetadata{}
}

func (IntervalLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.Interval && !.IsSet
}

func (IntervalLogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.FixedLenByteArray &&  == 12
}

func (IntervalLogicalType) () *format.LogicalType {
	panic("no parquet IntervalLogicalType yet implemented")
}

func (IntervalLogicalType) ( LogicalType) bool {
	,  := .(IntervalLogicalType)
	return 
}

// Float16LogicalType can only be used with a FixedLength byte array column
// that is exactly 2 bytes long
type Float16LogicalType struct{ baseLogicalType }

func (Float16LogicalType) () SortOrder {
	return SortSIGNED
}

func (Float16LogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": Float16LogicalType{}.String()})
}

func (Float16LogicalType) () string {
	return "Float16"
}

func (Float16LogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.None, DecimalMetadata{}
}

func (Float16LogicalType) ( ConvertedType,  DecimalMetadata) bool {
	if .IsSet {
		return false
	}
	switch  {
	case ConvertedTypes.None, ConvertedTypes.NA:
		return true
	}
	return false
}

func (Float16LogicalType) ( parquet.Type,  int32) bool {
	return  == parquet.Types.FixedLenByteArray &&  == 2
}

func (Float16LogicalType) () *format.LogicalType {
	return &format.LogicalType{FLOAT16: format.NewFloat16Type()}
}

func (Float16LogicalType) ( LogicalType) bool {
	,  := .(Float16LogicalType)
	return 
}

type VariantLogicalType struct{ baseLogicalType }

func (VariantLogicalType) () bool { return true }

func (VariantLogicalType) () SortOrder {
	return SortUNKNOWN
}

func (VariantLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": VariantLogicalType{}.String()})
}

func (VariantLogicalType) () string {
	return "Variant"
}

func (VariantLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.None, DecimalMetadata{}
}

func (VariantLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.None
}

func (VariantLogicalType) (parquet.Type, int32) bool { return false }

func (VariantLogicalType) () *format.LogicalType {
	return &format.LogicalType{VARIANT: &format.VariantType{SpecificationVersion: thrift.Int8Ptr(1)}}
}

func (VariantLogicalType) ( LogicalType) bool {
	,  := .(VariantLogicalType)
	return 
}

type NullLogicalType struct{ baseLogicalType }

func (NullLogicalType) () SortOrder {
	return SortUNKNOWN
}

func (NullLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": NullLogicalType{}.String()})
}

func (NullLogicalType) () string {
	return "Null"
}

func (NullLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.None, DecimalMetadata{}
}

func (NullLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	if .IsSet {
		return false
	}
	switch  {
	case ConvertedTypes.None, ConvertedTypes.NA:
		return true
	}
	return false
}

func (NullLogicalType) (parquet.Type, int32) bool {
	return true
}

func (NullLogicalType) () *format.LogicalType {
	return &format.LogicalType{UNKNOWN: format.NewNullType()}
}

func (NullLogicalType) ( LogicalType) bool {
	,  := .(NullLogicalType)
	return 
}

type NoLogicalType struct{ baseLogicalType }

func (NoLogicalType) () SortOrder {
	return SortUNKNOWN
}

func (NoLogicalType) () ([]byte, error) {
	return json.Marshal(map[string]string{"Type": NoLogicalType{}.String()})
}

func (NoLogicalType) () bool { return false }

func (NoLogicalType) () string {
	return "None"
}

func (NoLogicalType) () (ConvertedType, DecimalMetadata) {
	return ConvertedTypes.None, DecimalMetadata{}
}

func (NoLogicalType) ( ConvertedType,  DecimalMetadata) bool {
	return  == ConvertedTypes.None && !.IsSet
}

func (NoLogicalType) (parquet.Type, int32) bool {
	return true
}

func (NoLogicalType) () *format.LogicalType {
	panic("cannot convert NoLogicalType to thrift")
}

func (NoLogicalType) ( LogicalType) bool {
	,  := .(NoLogicalType)
	return 
}

func (NoLogicalType) () bool { return true }