src/Polyrat.ts   F
last analyzed

Complexity

Total Complexity 81
Complexity/F 10.13

Size

Lines of Code 233
Function Count 8

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 145
dl 0
loc 233
ccs 109
cts 109
cp 1
rs 2
c 0
b 0
f 0
wmc 81
mnd 73
bc 73
fnc 8
bpm 9.125
cpm 10.125
noi 0

8 Functions

Rating   Name   Duplication   Size   Complexity  
F Polyrat.toHTMLFormula 0 29 14
F Polyrat.toCalcFormula 0 29 14
A Polyrat.toString 0 6 1
D Polyrat.evaluate 0 19 13
F Polyrat.toStandardAlphaFormHTML 0 38 23
A Polyrat.clone 0 6 1
D Polyrat.toGLSLFormula 0 34 13
A Polyrat.toJSON 0 11 2

How to fix   Complexity   

Complexity

Complex classes like src/Polyrat.ts often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1 3
import { Rat } from './Rat'
2 3
import Symbolizer from './Symbolizer'
3
4
export interface Coefficents {
5
  [Key: string]: bigint
6
}
7
8
/**
9
 * @class Rational polynumber
10
 * @name Polyrat
11
 */
12 3
export class Polyrat {
13
  // coefficent values are indexed with their the exponents in each dimension, comma-separated, as the key
14 21
  coefficents: Coefficents = {}
15
16
  // the dimension is how many params there are, defined by the length of the exponent keys
17 21
  dimension = 0
18
19
  // unique symbols for each dimension
20 21
  latinSymbols = ''
21 21
  greekSymbols = ''
22
23
  /**
24
   * Initialize a rational polynumber.
25
   */
26
  constructor(coefficents?: Coefficents) {
27 21
    if (coefficents) {
28 19
      this.coefficents = coefficents
29
    }
30 21
    if (Object.keys(this.coefficents).length) {
31 14
      const ck = Object.keys(this.coefficents)
32 14
      this.dimension = ck[0] ? ck[0].split(',').length : 0
33
    }
34 21
    const lsg = new Symbolizer('xyzw').generator()
35 21
    for (let i = 0; i < this.dimension; i++) {
36 21
      this.latinSymbols += lsg.next().value
37
    }
38 21
    const gsg = new Symbolizer().generator()
39 21
    for (let i = 0; i < this.dimension; i++) {
40 21
      this.greekSymbols += gsg.next().value
41
    }
42
  }
43
44
  /**
45
   * Evaluate the result given the parameters for each dimension.
46
   */
47
  evaluate(parameters: Rat[]): Rat {
48 1
    let result: Rat = new Rat()
49 1
    for (const [exponents, coefficent] of Object.entries(this.coefficents)) {
50 1
      let value: Rat = new Rat(coefficent)
51 1
      const dimensions = exponents.split(',')
52 1
      for (let i = 0; i < dimensions.length; i++) {
53 1
        if (i in parameters) {
54 2
          const base = parameters[i] ?? new Rat(1)
55 2
          const dimension = parseInt(dimensions[i] ?? '1', 10)
56 1
          value = value.mul(base.pow(new Rat(dimension)))
57
        }
58
      }
59 1
      result = result.add(value)
60
    }
61 1
    return result
62
  }
63
64
  /**s
65
   * The text representation.
66
   */
67
  toString(): string {
68 2
    return `${this.constructor.name}(${this.toJSON()})`
69
  }
70
71
  /**
72
   * The JSON representation.
73
   */
74
  toJSON(): string {
75
    // return JSON.stringify(this.coefficents)
76 4
    const r = []
77 4
    for (const [exponents, coefficent] of Object.entries(this.coefficents)) {
78 12
      r.push(`"${exponents}":"${coefficent.toString()}"`)
79
    }
80 4
    return `{${r.join(',')}}`
81
  }
82
83
  /**
84
   * The formula in the human way with exponents as HTML sups.
85
   */
86
  toHTMLFormula(): string {
87 5
    const r: string[] = []
88 5
    for (const [exponents, coefficent] of Object.entries(this.coefficents)) {
89 12
      const t: string[] = []
90 12
      const f = coefficent.toString()
91 12
      if (f !== '1') t.push(f)
92 12
      const dimensions = exponents.split(',')
93 12
      for (let i = 0; i < dimensions.length; i++) {
94 24
        if (dimensions[i] !== '0') {
95 12
          if (dimensions[i] === '1') {
96 3
            t.push(this.latinSymbols[i] ?? '?')
97
          } else {
98 9
            t.push(
99
              `${this.latinSymbols[i] ?? '?'}<sup>${parseInt(
100
                dimensions[i] ?? '?',
101
                10,
102
              )}</sup>`,
103
            )
104
          }
105
        }
106
      }
107 12
      if (t) r.push(t.join(''))
108
    }
109 5
    if (r.length === 0) return '0'
110 4
    return r.join(' + ')
111
  }
112
113
  /**
114
   * The formula in the standard alpha form as HTML.
115
   */
116
  toStandardAlphaFormHTML(): string {
117 8
    const rn: string[] = []
118 8
    const rd: string[] = []
119 8
    if (!Object.keys(this.coefficents).length) return '0'
120 7
    for (const [exponents, coefficent] of Object.entries(this.coefficents)) {
121 14
      const tn: string[] = []
122 14
      const td: string[] = []
123 14
      const f = coefficent.toString()
124 14
      if (f !== '1') tn.push(f)
125 14
      const dimensions = exponents.split(',')
126 14
      for (let i = 0; i < dimensions.length; i++) {
127 25
        if (dimensions[i] !== '0') {
128 14
          if (dimensions[i] === '1') {
129 4
            tn.push(this.greekSymbols[i] ?? '?')
130
          } else {
131 10
            const exponent = parseInt(dimensions[i] ?? '0', 10)
132 10
            if (exponent > 0) {
133 7
              tn.push(`${this.greekSymbols[i] ?? '?'}<sup>${exponent}</sup>`)
134
            } else {
135 3
              if (exponent === -1) {
136 2
                td.push(this.greekSymbols[i] ?? '?')
137
              } else {
138 2
                td.push(`${this.greekSymbols[i] ?? '?'}<sup>${-exponent}</sup>`)
139
              }
140
            }
141
          }
142
        }
143
      }
144 14
      if (tn.length) rn.push(tn.join(''))
145 14
      if (td.length) rd.push(td.join(''))
146
    }
147 7
    if (rn.length === 0) rn.push('1')
148 7
    if (rd.length === 0) return rn.join(' + ')
149 3
    return rn.join(' + ') + ' / ' + rd.join(' + ')
150
  }
151
152
  /**
153
   * The "calc" code for evaluating the value.
154
   */
155
  toCalcFormula(): string {
156 5
    const r: string[] = []
157 5
    for (const [exponents, coefficent] of Object.entries(this.coefficents)) {
158 11
      const t: string[] = []
159 11
      const f = coefficent.toString()
160 11
      if (f !== '1') t.push(f)
161 11
      const dimensions = exponents.split(',')
162 11
      for (let i = 0; i < dimensions.length; i++) {
163 20
        if (dimensions[i] !== '0') {
164 11
          if (dimensions[i] === '1') {
165 4
            t.push(this.latinSymbols[i] ?? '?')
166
          } else {
167 7
            t.push(
168
              `${this.latinSymbols[i] ?? '?'}^${parseInt(
169
                dimensions[i] ?? '0',
170
                10,
171
              )}`,
172
            )
173
          }
174
        }
175
      }
176 11
      if (t.length) r.push(t.join('*'))
177
    }
178 5
    if (r.length === 0) return '0'
179 4
    return r.join(' + ')
180
  }
181
182
  /**
183
   * The GLSL code for evaluating the value.
184
   */
185
  toGLSLFormula(): string {
186 3
    const r: string[] = []
187 3
    for (const [exponents, coefficent] of Object.entries(this.coefficents)) {
188 7
      const t: string[] = []
189 7
      const f = coefficent.toString()
190 7
      if (f !== '1') t.push(f + '.0')
191 7
      const dimensions = exponents.split(',')
192 7
      for (let i = 0; i < dimensions.length; i++) {
193 14
        if (dimensions[i] !== '0') {
194 8
          let exponent = parseInt(dimensions[i] ?? '0', 10)
195 8
          const recipricol = exponent < 0
196
          // pow doesn't work for < 0
197
          // t.push(`pow(${this.symbols[i]},${exponent}.0)`)
198
          // muliply instead
199 8
          if (recipricol) {
200 1
            t.push('1.0/(1.0')
201 1
            exponent = -exponent
202
          }
203 8
          t.push(
204
            (this.latinSymbols[i] ?? '?').repeat(exponent).split('').join('*'),
205
          )
206 8
          if (recipricol) {
207 1
            t.push('1.0)')
208
          }
209
        }
210
      }
211 7
      if (t.length) r.push(t.join('*'))
212
    }
213 3
    if (r.length === 0) return '0.0'
214 2
    return r.join('+')
215
  }
216
217
  /**
218
   * Clone this.
219
   */
220
  clone(): Polyrat {
221 1
    return new Polyrat(this.coefficents)
222
  }
223
}
224
225
/**
226
 * Parse the string and return it as a Polyrat.
227
 */
228 3
export const stringToPolyrat = (s: string): Polyrat => {
229 1
  return new Polyrat(JSON.parse(s) as Coefficents)
230
}
231
232
export default Polyrat
233