gonum.org/v1/gonum@v0.14.0/num/quat/quat.go (about)

     1  // Copyright ©2018 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package quat
     6  
     7  import (
     8  	"fmt"
     9  	"strconv"
    10  	"strings"
    11  )
    12  
    13  var zero Number
    14  
    15  // Number is a float64 precision quaternion.
    16  type Number struct {
    17  	Real, Imag, Jmag, Kmag float64
    18  }
    19  
    20  // Format implements fmt.Formatter.
    21  func (q Number) Format(fs fmt.State, c rune) {
    22  	prec, pOk := fs.Precision()
    23  	if !pOk {
    24  		prec = -1
    25  	}
    26  	width, wOk := fs.Width()
    27  	if !wOk {
    28  		width = -1
    29  	}
    30  	switch c {
    31  	case 'v':
    32  		if fs.Flag('#') {
    33  			fmt.Fprintf(fs, "%T{Real:%#v, Imag:%#v, Jmag:%#v, Kmag:%#v}", q, q.Real, q.Imag, q.Jmag, q.Kmag)
    34  			return
    35  		}
    36  		if fs.Flag('+') {
    37  			fmt.Fprintf(fs, "{Real:%+v, Imag:%+v, Jmag:%+v, Kmag:%+v}", q.Real, q.Imag, q.Jmag, q.Kmag)
    38  			return
    39  		}
    40  		c = 'g'
    41  		prec = -1
    42  		fallthrough
    43  	case 'e', 'E', 'f', 'F', 'g', 'G':
    44  		fre := fmtString(fs, c, prec, width, false)
    45  		fim := fmtString(fs, c, prec, width, true)
    46  		fmt.Fprintf(fs, fmt.Sprintf("(%s%[2]si%[2]sj%[2]sk)", fre, fim), q.Real, q.Imag, q.Jmag, q.Kmag)
    47  	default:
    48  		fmt.Fprintf(fs, "%%!%c(%T=%[2]v)", c, q)
    49  		return
    50  	}
    51  }
    52  
    53  // This is horrible, but it's what we have.
    54  func fmtString(fs fmt.State, c rune, prec, width int, wantPlus bool) string {
    55  	var b strings.Builder
    56  	b.WriteByte('%')
    57  	for _, f := range "0+- " {
    58  		if fs.Flag(int(f)) || (f == '+' && wantPlus) {
    59  			b.WriteByte(byte(f))
    60  		}
    61  	}
    62  	if width >= 0 {
    63  		fmt.Fprint(&b, width)
    64  	}
    65  	if prec >= 0 {
    66  		b.WriteByte('.')
    67  		if prec > 0 {
    68  			fmt.Fprint(&b, prec)
    69  		}
    70  	}
    71  	b.WriteRune(c)
    72  	return b.String()
    73  }
    74  
    75  // Add returns the sum of x and y.
    76  func Add(x, y Number) Number {
    77  	return Number{
    78  		Real: x.Real + y.Real,
    79  		Imag: x.Imag + y.Imag,
    80  		Jmag: x.Jmag + y.Jmag,
    81  		Kmag: x.Kmag + y.Kmag,
    82  	}
    83  }
    84  
    85  // Sub returns the difference of x and y, x-y.
    86  func Sub(x, y Number) Number {
    87  	return Number{
    88  		Real: x.Real - y.Real,
    89  		Imag: x.Imag - y.Imag,
    90  		Jmag: x.Jmag - y.Jmag,
    91  		Kmag: x.Kmag - y.Kmag,
    92  	}
    93  }
    94  
    95  // Mul returns the Hamiltonian product of x and y.
    96  func Mul(x, y Number) Number {
    97  	return Number{
    98  		Real: x.Real*y.Real - x.Imag*y.Imag - x.Jmag*y.Jmag - x.Kmag*y.Kmag,
    99  		Imag: x.Real*y.Imag + x.Imag*y.Real + x.Jmag*y.Kmag - x.Kmag*y.Jmag,
   100  		Jmag: x.Real*y.Jmag - x.Imag*y.Kmag + x.Jmag*y.Real + x.Kmag*y.Imag,
   101  		Kmag: x.Real*y.Kmag + x.Imag*y.Jmag - x.Jmag*y.Imag + x.Kmag*y.Real,
   102  	}
   103  }
   104  
   105  // Scale returns q scaled by f.
   106  func Scale(f float64, q Number) Number {
   107  	return Number{Real: f * q.Real, Imag: f * q.Imag, Jmag: f * q.Jmag, Kmag: f * q.Kmag}
   108  }
   109  
   110  // Parse converts the string s to a Number. The string may be parenthesized and
   111  // has the format [±]N±Ni±Nj±Nk. The order of the components is not strict.
   112  func Parse(s string) (Number, error) {
   113  	if len(s) == 0 {
   114  		return Number{}, parseError{state: -1}
   115  	}
   116  	orig := s
   117  
   118  	wantClose := s[0] == '('
   119  	if wantClose {
   120  		if s[len(s)-1] != ')' {
   121  			return Number{}, parseError{string: orig, state: -1}
   122  		}
   123  		s = s[1 : len(s)-1]
   124  	}
   125  	if len(s) == 0 {
   126  		return Number{}, parseError{string: orig, state: -1}
   127  	}
   128  	switch s[0] {
   129  	case 'n', 'N':
   130  		if strings.ToLower(s) == "nan" {
   131  			return NaN(), nil
   132  		}
   133  	case 'i', 'I':
   134  		if strings.ToLower(s) == "inf" {
   135  			return Inf(), nil
   136  		}
   137  	}
   138  
   139  	var q Number
   140  	var parts byte
   141  	for i := 0; i < 4; i++ {
   142  		beg, end, p, err := floatPart(s)
   143  		if err != nil {
   144  			return q, parseError{string: orig, state: -1}
   145  		}
   146  		if parts&(1<<p) != 0 {
   147  			return q, parseError{string: orig, state: -1}
   148  		}
   149  		parts |= 1 << p
   150  		var v float64
   151  		switch s[:end] {
   152  		case "-":
   153  			if len(s[end:]) == 0 {
   154  				return q, parseError{string: orig, state: -1}
   155  			}
   156  			v = -1
   157  		case "+":
   158  			if len(s[end:]) == 0 {
   159  				return q, parseError{string: orig, state: -1}
   160  			}
   161  			v = 1
   162  		default:
   163  			v, err = strconv.ParseFloat(s[beg:end], 64)
   164  			if err != nil {
   165  				return q, err
   166  			}
   167  		}
   168  		s = s[end:]
   169  		switch p {
   170  		case 0:
   171  			q.Real = v
   172  		case 1:
   173  			q.Imag = v
   174  			s = s[1:]
   175  		case 2:
   176  			q.Jmag = v
   177  			s = s[1:]
   178  		case 3:
   179  			q.Kmag = v
   180  			s = s[1:]
   181  		}
   182  		if len(s) == 0 {
   183  			return q, nil
   184  		}
   185  		if !isSign(rune(s[0])) {
   186  			return q, parseError{string: orig, state: -1}
   187  		}
   188  	}
   189  
   190  	return q, parseError{string: orig, state: -1}
   191  }
   192  
   193  func floatPart(s string) (beg, end int, part uint, err error) {
   194  	const (
   195  		wantMantSign = iota
   196  		wantMantIntInit
   197  		wantMantInt
   198  		wantMantFrac
   199  		wantExpSign
   200  		wantExpInt
   201  
   202  		wantInfN
   203  		wantInfF
   204  		wantCloseInf
   205  
   206  		wantNaNA
   207  		wantNaNN
   208  		wantCloseNaN
   209  	)
   210  	var i, state int
   211  	var r rune
   212  	for i, r = range s {
   213  		switch state {
   214  		case wantMantSign:
   215  			switch {
   216  			default:
   217  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   218  			case isSign(r):
   219  				state = wantMantIntInit
   220  			case isDigit(r):
   221  				state = wantMantInt
   222  			case isDot(r):
   223  				state = wantMantFrac
   224  			case r == 'i', r == 'I':
   225  				state = wantInfN
   226  			case r == 'n', r == 'N':
   227  				state = wantNaNA
   228  			}
   229  
   230  		case wantMantIntInit:
   231  			switch {
   232  			default:
   233  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   234  			case isDigit(r):
   235  				state = wantMantInt
   236  			case isDot(r):
   237  				state = wantMantFrac
   238  			case r == 'i':
   239  				// We need to sneak a look-ahead here.
   240  				if i == len(s)-1 || s[i+1] == '-' || s[i+1] == '+' {
   241  					return 0, i, 1, nil
   242  				}
   243  				fallthrough
   244  			case r == 'I':
   245  				state = wantInfN
   246  			case r == 'n', r == 'N':
   247  				state = wantNaNA
   248  			}
   249  
   250  		case wantMantInt:
   251  			switch {
   252  			default:
   253  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   254  			case isDigit(r):
   255  				// Do nothing
   256  			case isDot(r):
   257  				state = wantMantFrac
   258  			case isExponent(r):
   259  				state = wantExpSign
   260  			case isSign(r):
   261  				return 0, i, 0, nil
   262  			case r == 'i':
   263  				return 0, i, 1, nil
   264  			case r == 'j':
   265  				return 0, i, 2, nil
   266  			case r == 'k':
   267  				return 0, i, 3, nil
   268  			}
   269  
   270  		case wantMantFrac:
   271  			switch {
   272  			default:
   273  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   274  			case isDigit(r):
   275  				// Do nothing
   276  			case isExponent(r):
   277  				state = wantExpSign
   278  			case isSign(r):
   279  				return 0, i, 0, nil
   280  			case r == 'i':
   281  				return 0, i, 1, nil
   282  			case r == 'j':
   283  				return 0, i, 2, nil
   284  			case r == 'k':
   285  				return 0, i, 3, nil
   286  			}
   287  
   288  		case wantExpSign:
   289  			switch {
   290  			default:
   291  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   292  			case isSign(r) || isDigit(r):
   293  				state = wantExpInt
   294  			}
   295  
   296  		case wantExpInt:
   297  			switch {
   298  			default:
   299  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   300  			case isDigit(r):
   301  				// Do nothing
   302  			case isSign(r):
   303  				return 0, i, 0, nil
   304  			case r == 'i':
   305  				return 0, i, 1, nil
   306  			case r == 'j':
   307  				return 0, i, 2, nil
   308  			case r == 'k':
   309  				return 0, i, 3, nil
   310  			}
   311  
   312  		case wantInfN:
   313  			if r != 'n' && r != 'N' {
   314  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   315  			}
   316  			state = wantInfF
   317  		case wantInfF:
   318  			if r != 'f' && r != 'F' {
   319  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   320  			}
   321  			state = wantCloseInf
   322  		case wantCloseInf:
   323  			switch {
   324  			default:
   325  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   326  			case isSign(r):
   327  				return 0, i, 0, nil
   328  			case r == 'i':
   329  				return 0, i, 1, nil
   330  			case r == 'j':
   331  				return 0, i, 2, nil
   332  			case r == 'k':
   333  				return 0, i, 3, nil
   334  			}
   335  
   336  		case wantNaNA:
   337  			if r != 'a' && r != 'A' {
   338  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   339  			}
   340  			state = wantNaNN
   341  		case wantNaNN:
   342  			if r != 'n' && r != 'N' {
   343  				return 0, i, 0, parseError{string: s, state: state, rune: r}
   344  			}
   345  			state = wantCloseNaN
   346  		case wantCloseNaN:
   347  			if isSign(rune(s[0])) {
   348  				beg = 1
   349  			}
   350  			switch {
   351  			default:
   352  				return beg, i, 0, parseError{string: s, state: state, rune: r}
   353  			case isSign(r):
   354  				return beg, i, 0, nil
   355  			case r == 'i':
   356  				return beg, i, 1, nil
   357  			case r == 'j':
   358  				return beg, i, 2, nil
   359  			case r == 'k':
   360  				return beg, i, 3, nil
   361  			}
   362  		}
   363  	}
   364  	switch state {
   365  	case wantMantSign, wantExpSign, wantExpInt:
   366  		if state == wantExpInt && isDigit(r) {
   367  			break
   368  		}
   369  		return 0, i, 0, parseError{string: s, state: state, rune: r}
   370  	}
   371  	return 0, len(s), 0, nil
   372  }
   373  
   374  func isSign(r rune) bool {
   375  	return r == '+' || r == '-'
   376  }
   377  
   378  func isDigit(r rune) bool {
   379  	return '0' <= r && r <= '9'
   380  }
   381  
   382  func isExponent(r rune) bool {
   383  	return r == 'e' || r == 'E'
   384  }
   385  
   386  func isDot(r rune) bool {
   387  	return r == '.'
   388  }
   389  
   390  type parseError struct {
   391  	string string
   392  	state  int
   393  	rune   rune
   394  }
   395  
   396  func (e parseError) Error() string {
   397  	if e.state < 0 {
   398  		return fmt.Sprintf("quat: failed to parse: %q", e.string)
   399  	}
   400  	return fmt.Sprintf("quat: failed to parse in state %d with %q: %q", e.state, e.rune, e.string)
   401  }