github.com/richardwilkes/toolbox@v1.121.0/xmath/fixed/f128/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 f128 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 var zero Int[T] 41 if f.Denominator == zero { 42 f.Numerator = Int[T]{} 43 f.Denominator = From[T, int](1) 44 } else if f.Denominator.LessThan(zero) { 45 negOne := From[T, int](-1) 46 f.Numerator = f.Numerator.Mul(negOne) 47 f.Denominator = f.Denominator.Mul(negOne) 48 } 49 } 50 51 // Value returns the computed value. 52 func (f Fraction[T]) Value() Int[T] { 53 n := f 54 n.Normalize() 55 return n.Numerator.Div(n.Denominator) 56 } 57 58 // StringWithSign returns the same as String(), but prefixes the value with a '+' if it is positive. 59 func (f Fraction[T]) StringWithSign() string { 60 n := f 61 n.Normalize() 62 s := n.Numerator.StringWithSign() 63 if n.Denominator == From[T, int](1) { 64 return s 65 } 66 return s + "/" + n.Denominator.String() 67 } 68 69 func (f Fraction[T]) String() string { 70 n := f 71 n.Normalize() 72 s := n.Numerator.String() 73 if n.Denominator == From[T, int](1) { 74 return s 75 } 76 return s + "/" + n.Denominator.String() 77 } 78 79 // MarshalJSON implements json.Marshaler. 80 func (f Fraction[T]) MarshalJSON() ([]byte, error) { 81 return json.Marshal(f.String()) 82 } 83 84 // UnmarshalJSON implements json.Unmarshaler. 85 func (f *Fraction[T]) UnmarshalJSON(in []byte) error { 86 var s string 87 if err := json.Unmarshal(in, &s); err != nil { 88 return err 89 } 90 *f = NewFraction[T](s) 91 f.Normalize() 92 return nil 93 }