RosettaCodeData/Task/Currency/Go/currency.go

89 lines
1.9 KiB
Go

package main
import (
"fmt"
"log"
"math/big"
)
// DC for dollars and cents. Value is an integer number of cents.
type DC int64
func (dc DC) String() string {
d := dc / 100
if dc < 0 {
dc = -dc
}
return fmt.Sprintf("%d.%02d", d, dc%100)
}
// Extend returns extended price of a unit price.
func (dc DC) Extend(n int) DC {
return dc * DC(n)
}
var one = big.NewInt(1)
var hundred = big.NewRat(100, 1)
// ParseDC parses dollars and cents as a string into a DC.
func ParseDC(s string) (DC, bool) {
r, ok := new(big.Rat).SetString(s)
if !ok {
return 0, false
}
r.Mul(r, hundred)
if r.Denom().Cmp(one) != 0 {
return 0, false
}
return DC(r.Num().Int64()), true
}
// TR for tax rate. Value is an an exact rational.
type TR struct {
*big.Rat
}
func NewTR() TR {
return TR{new(big.Rat)}
}
// SetString overrides Rat.SetString to return the TR type.
func (tr TR) SetString(s string) (TR, bool) {
if _, ok := tr.Rat.SetString(s); !ok {
return TR{}, false
}
return tr, true
}
var half = big.NewRat(1, 2)
// Tax computes a tax amount, rounding to the nearest cent.
func (tr TR) Tax(dc DC) DC {
r := big.NewRat(int64(dc), 1)
r.Add(r.Mul(r, tr.Rat), half)
return DC(new(big.Int).Div(r.Num(), r.Denom()).Int64())
}
func main() {
hamburgerPrice, ok := ParseDC("5.50")
if !ok {
log.Fatal("Invalid hamburger price")
}
milkshakePrice, ok := ParseDC("2.86")
if !ok {
log.Fatal("Invalid milkshake price")
}
taxRate, ok := NewTR().SetString("0.0765")
if !ok {
log.Fatal("Invalid tax rate")
}
totalBeforeTax := hamburgerPrice.Extend(4000000000000000) +
milkshakePrice.Extend(2)
tax := taxRate.Tax(totalBeforeTax)
total := totalBeforeTax + tax
fmt.Printf("Total before tax: %22s\n", totalBeforeTax)
fmt.Printf(" Tax: %22s\n", tax)
fmt.Printf(" Total: %22s\n", total)
}