github.com/richardwilkes/toolbox@v1.121.0/xmath/fixed/f64/fraction.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package f64 11 12 import ( 13 "encoding/json" 14 "strings" 15 16 "github.com/richardwilkes/toolbox/xmath/fixed" 17 ) 18 19 // Fraction holds a fractional value. 20 type Fraction[T fixed.Dx] struct { 21 Numerator Int[T] 22 Denominator Int[T] 23 } 24 25 // NewFraction creates a new fractional value from a string. 26 func NewFraction[T fixed.Dx](s string) Fraction[T] { 27 parts := strings.SplitN(s, "/", 2) 28 f := Fraction[T]{ 29 Numerator: FromStringForced[T](strings.TrimSpace(parts[0])), 30 Denominator: From[T, int](1), 31 } 32 if len(parts) > 1 { 33 f.Denominator = FromStringForced[T](strings.TrimSpace(parts[1])) 34 } 35 return f 36 } 37 38 // Normalize the fraction, eliminating any division by zero and ensuring a positive denominator. 39 func (f *Fraction[T]) Normalize() { 40 if f.Denominator == 0 { 41 f.Numerator = 0 42 f.Denominator = From[T, int](1) 43 } else if f.Denominator < 0 { 44 negOne := From[T, int](-1) 45 f.Numerator = f.Numerator.Mul(negOne) 46 f.Denominator = f.Denominator.Mul(negOne) 47 } 48 } 49 50 // Value returns the computed value. 51 func (f Fraction[T]) Value() Int[T] { 52 n := f 53 n.Normalize() 54 return n.Numerator.Div(n.Denominator) 55 } 56 57 // StringWithSign returns the same as String(), but prefixes the value with a '+' if it is positive. 58 func (f Fraction[T]) StringWithSign() string { 59 n := f 60 n.Normalize() 61 s := n.Numerator.StringWithSign() 62 if n.Denominator == From[T, int](1) { 63 return s 64 } 65 return s + "/" + n.Denominator.String() 66 } 67 68 func (f Fraction[T]) String() string { 69 n := f 70 n.Normalize() 71 s := n.Numerator.String() 72 if n.Denominator == From[T, int](1) { 73 return s 74 } 75 return s + "/" + n.Denominator.String() 76 } 77 78 // MarshalJSON implements json.Marshaler. 79 func (f Fraction[T]) MarshalJSON() ([]byte, error) { 80 return json.Marshal(f.String()) 81 } 82 83 // UnmarshalJSON implements json.Unmarshaler. 84 func (f *Fraction[T]) UnmarshalJSON(in []byte) error { 85 var s string 86 if err := json.Unmarshal(in, &s); err != nil { 87 return err 88 } 89 *f = NewFraction[T](s) 90 f.Normalize() 91 return nil 92 }