1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package decimal128 // import "github.com/apache/arrow/go/v6/arrow/decimal128"
18
19import (
20	"math/big"
21)
22
23var (
24	MaxDecimal128 = New(542101086242752217, 687399551400673280-1)
25)
26
27// Num represents a signed 128-bit integer in two's complement.
28// Calculations wrap around and overflow is ignored.
29//
30// For a discussion of the algorithms, look at Knuth's volume 2,
31// Semi-numerical Algorithms section 4.3.1.
32//
33// Adapted from the Apache ORC C++ implementation
34type Num struct {
35	lo uint64 // low bits
36	hi int64  // high bits
37}
38
39// New returns a new signed 128-bit integer value.
40func New(hi int64, lo uint64) Num {
41	return Num{lo: lo, hi: hi}
42}
43
44// FromU64 returns a new signed 128-bit integer value from the provided uint64 one.
45func FromU64(v uint64) Num {
46	return New(0, v)
47}
48
49// FromI64 returns a new signed 128-bit integer value from the provided int64 one.
50func FromI64(v int64) Num {
51	switch {
52	case v > 0:
53		return New(0, uint64(v))
54	case v < 0:
55		return New(-1, uint64(v))
56	default:
57		return Num{}
58	}
59}
60
61// FromBigInt will convert a big.Int to a Num, if the value in v has a
62// BitLen > 128, this will panic.
63func FromBigInt(v *big.Int) (n Num) {
64	bitlen := v.BitLen()
65	if bitlen > 128 {
66		panic("arrow/decimal128: cannot represent value larger than 128bits")
67	} else if bitlen == 0 {
68		// if bitlen is 0, then the value is 0 so return the default zeroed
69		// out n
70		return
71	}
72
73	// if the value is negative, then get the high and low bytes from
74	// v, and then negate it. this is because Num uses a two's compliment
75	// representation of values and big.Int stores the value as a bool for
76	// the sign and the absolute value of the integer. This means that the
77	// raw bytes are *always* the absolute value.
78	b := v.Bits()
79	n.lo = uint64(b[0])
80	if len(b) > 1 {
81		n.hi = int64(b[1])
82	}
83	if v.Sign() < 0 {
84		return n.negated()
85	}
86	return
87}
88
89func (n Num) negated() Num {
90	n.lo = ^n.lo + 1
91	n.hi = ^n.hi
92	if n.lo == 0 {
93		n.hi += 1
94	}
95	return n
96}
97
98// LowBits returns the low bits of the two's complement representation of the number.
99func (n Num) LowBits() uint64 { return n.lo }
100
101// HighBits returns the high bits of the two's complement representation of the number.
102func (n Num) HighBits() int64 { return n.hi }
103
104// Sign returns:
105//
106// -1 if x <  0
107//  0 if x == 0
108// +1 if x >  0
109func (n Num) Sign() int {
110	if n == (Num{}) {
111		return 0
112	}
113	return int(1 | (n.hi >> 63))
114}
115
116func toBigIntPositive(n Num) *big.Int {
117	return (&big.Int{}).SetBits([]big.Word{big.Word(n.lo), big.Word(n.hi)})
118}
119
120// while the code would be simpler to just do lsh/rsh and add
121// it turns out from benchmarking that calling SetBits passing
122// in the words and negating ends up being >2x faster
123func (n Num) BigInt() *big.Int {
124	if n.Sign() < 0 {
125		b := toBigIntPositive(n.negated())
126		return b.Neg(b)
127	}
128	return toBigIntPositive(n)
129}
130