/vqdr/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/vqdr/trunk

« back to all changes in this revision

Viewing changes to src/common/fast-number.vala

  • Committer: Gustav Hartvigsson
  • Date: 2020-06-07 18:48:24 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20200607184824-jf14f7a1b1di2i2q
* Initial code - far from done

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * The contects of this file is in the Public Domain.
3
 
 *
4
 
 * Created by Gustav Hartivgsson.
5
 
 */
6
 
using GLib;
7
 
[CCode (cname = "V", cprefix = "v_")]
8
 
namespace Utils {
 
1
namespace VQDR.Common {
9
2
  
10
3
  /**
11
 
   * Fast Numbers are a decimal representation of numbers in the form of
12
 
   * a normal integer value. All internal numbers are multiples of 1000, so the
13
 
   * there is room for two three decimal points.
14
 
   * 
15
 
   * Maths done on these numbers are done using standard integer operations, and
16
 
   * not floating point maths.
17
 
   * 
18
 
   * The decimal part of the FastNumber has a maximum of 3 decimals.
19
 
   * 
20
 
   * How the value is divided internally is as follows:
21
 
   * {{{
22
 
   *  (base 10) [0 0 0 0 0 .... 0 | 0 0 0  ]
23
 
   *            [non-decial part  | decimal]
24
 
   * }}}
25
 
   *
 
4
   * Fast Numbers are a decimal representanion of numbers in the folm of 
 
5
   * a normal integer value. All internal nummbers are multiples of 1000, so the
 
6
   * there is room for two three decimal ponts.
 
7
   * 
 
8
   * Math done on these numbers are done using standard integer operations, and
 
9
   * not floating point math.
26
10
   */
27
 
  [CCode (cname = "VFastNumber", cprefix = "v_fast_number_")]
28
 
  public struct FastNumber {
29
 
    
30
 
    /** Precision used to output values */
31
 
    public const int PRECISION_DIGITS = 2;
32
 
    
33
 
    /** Precision factor used to evaluate output */
34
 
    public const int PRECISION_FACTOR = 100;
35
 
    
36
 
    public const int MUL_FACTOR = PRECISION_FACTOR * 10;
37
 
    
38
 
    public long raw_number;
39
 
    
40
 
    public long leading_zeros;
41
 
    
42
 
    /* XXX
43
 
     * I'm not happy using getters/setters in this struct...
44
 
     * But I tink it'll have to do for simplicity.
45
 
     */
 
11
  class FastNumber {
 
12
    public const long MUL_FACTOR = 1000;
 
13
    
 
14
    public long raw_number { public get; private set; }
 
15
    
46
16
    public long number {
47
 
      public get {return (this.raw_number / MUL_FACTOR);}
48
 
      public set {
49
 
        this.raw_number = (MUL_FACTOR * value);
50
 
      }
51
 
    }
52
 
    
53
 
    /**
54
 
     * Initialises a FastNumber.
55
 
     *
56
 
     * Note: Due to implementation details, you can't pass a decimal part that
57
 
     *       is less than .1, as we are normalising decimal values to the
58
 
     *       correct place.
59
 
     * 
60
 
     * @param number   The number that are to be set as the none-decimal part of
61
 
     *                 the number. Defaults to 0.
62
 
     * 
63
 
     * @param decimal  The decimal part of the number. Defaults to 0.
64
 
     */
65
 
    public FastNumber (long number = 0) {
66
 
      if (number != 0) {
67
 
        this.raw_number = (number * MUL_FACTOR);
68
 
      } else {
69
 
        this.raw_number = 0;
70
 
      }
71
 
    }
72
 
    
73
 
    /**
74
 
     * Do a deep copy of a FastNumber.
75
 
     */
 
17
      get {return raw_number / MUL_FACTOR;}
 
18
      set {raw_number = number * MUL_FACTOR;}
 
19
    }
 
20
    
 
21
    public FastNumber (long val = 0) {
 
22
      this.number = val;
 
23
    }
 
24
    
76
25
    public FastNumber.copy (FastNumber other) {
77
26
      this.raw_number = other.raw_number;
78
27
    }
79
28
    
80
 
    /**
81
 
     * Initialises a FastNumber from a string.
82
 
     * 
83
 
     * Can be a decimal representation.
84
 
     */
85
29
    public FastNumber.from_string (string str) {
86
 
      parse_raw_number (str);
87
 
    }
88
 
    /**
89
 
     * Initialises a FastNumber with the internal representation of that number.
90
 
     */
91
 
    public FastNumber.raw (long raw) {
92
 
      this.raw_number = raw;
93
 
    }
94
 
    
95
 
    public FastNumber.from_float (double float_number) {
96
 
      // XXX Do we need a faster way of doing this?
97
 
      parse_raw_number (float_number.to_string ()); 
98
 
    }
99
 
    
100
 
    /**
101
 
     * Add this to an other FastNumber.
102
 
     * 
103
 
     * {{{
104
 
     * var f1 = FastNumber (3);   // f1 = 3
105
 
     * var f2 = FastNumber (2);   // f2 = 2
106
 
     * var f3 = f1.add (f2);      // f3 = 5
107
 
     * }}}
108
 
     * 
109
 
     * @return a newly initialised FastNumber.
110
 
     * 
111
 
     * @param other The other fast number you want to add to this value.
112
 
     */
113
 
    public FastNumber add (FastNumber? other) {
114
 
      if (other == null) {
115
 
        return  FastNumber.copy (this);
116
 
      }
117
 
      
118
 
      var v = FastNumber ();
119
 
      v.raw_number = (this.raw_number + other.raw_number);
120
 
      return v;
121
 
    }
122
 
    
123
 
    /**
124
 
     * Add this to an other FastNumber.
125
 
     * 
126
 
     * {{{
127
 
     * var f1 = FastNumber (3);   // f1 = 3
128
 
     * var f2 = FastNumber (2);   // f2 = 2
129
 
     * var f3 = f1.subtract (f2); // f3 = 1
130
 
     * }}}
131
 
     * 
132
 
     * @return a newly initialised FastNumber.
133
 
     * 
134
 
     * @param other  The other fast number you want to subtract from this 
135
 
     *               FastNumber.
136
 
     */
137
 
    public FastNumber subtract (FastNumber? other) {
138
 
      if (other == null) {
139
 
        return  FastNumber.copy (this);
140
 
      }
141
 
      
142
 
      var v = FastNumber ();
143
 
      v.raw_number = (this.raw_number - other.raw_number);
144
 
      return v;
145
 
    }
146
 
    
147
 
    /**
148
 
     * Multiply this FastNumber with another FastNumber.
149
 
     * 
150
 
     * {{{
151
 
     * var f1 = FastNumber (3);   // f1 = 3
152
 
     * var f2 = FastNumber (2);   // f2 = 2
153
 
     * var f3 = f1.multiply (f2); // f3 = 6
154
 
     * }}}
155
 
     * 
156
 
     * @return a newly initialised FastNumber.
157
 
     * 
158
 
     * @param other The value you want to multiply this value with.
159
 
     */
160
 
    public FastNumber multiply (FastNumber? other) {
161
 
      if (other == null || other.raw_number == 0 || this.raw_number == 0) {
162
 
        return  FastNumber ();
163
 
      }
164
 
      
165
 
      var ret = FastNumber ();
166
 
      ret.raw_number = ((this.raw_number * other.raw_number) / MUL_FACTOR);
167
 
      return ret;
168
 
    }
169
 
    
170
 
    /**
171
 
     * Divide this FastNumbers with another FastNumber.
172
 
     * 
173
 
     * {{{
174
 
     * var f1 = FastNumber (6);   // f1 = 6
175
 
     * var f2 = FastNumber (2);   // f2 = 2
176
 
     * var f3 = f1.multiply (f2); // f3 = 3
177
 
     * }}}
178
 
     * 
179
 
     * @return a newly initialised FastNumber.
180
 
     * 
181
 
     * @param other The value you want to multiply this value with.
182
 
     */
183
 
    public FastNumber divide (FastNumber other) throws MathError {
184
 
      if (other.raw_number == 0) {
185
 
        throw new MathError.DIVIDE_BY_ZERO ("trying to divide by zero");
186
 
      }
187
 
      var ret =  FastNumber ();
188
 
      ret.raw_number = ((this.raw_number * MUL_FACTOR) / other.raw_number);
189
 
      return ret;
190
 
    }
191
 
    
192
 
    [CCode (cname = "vqdr_common_fast_number_compare")]
193
 
    public long compare (FastNumber other) {
194
 
      return this.raw_number - other.raw_number;
195
 
    }
196
 
    
197
 
    /**
198
 
     * Round up this FastNumber and returns a new FastNumber.
199
 
     * 
200
 
     * @return a rounded up FastNumber.
201
 
     */
202
 
    public FastNumber round_up () {
203
 
      FastNumber ret;
204
 
      long decimal = raw_number % PRECISION_FACTOR;
205
 
      if (decimal > 0) {
206
 
        ret = FastNumber.raw (raw_number + PRECISION_FACTOR - decimal);
207
 
      } else {
208
 
        ret = FastNumber.raw (raw_number - decimal);
209
 
      }
210
 
      return ret;
211
 
    }
212
 
    
213
 
    /**
214
 
     * Round up this FastNumber and returns a new FastNumber.
215
 
     * 
216
 
     * @return a rounded down FastNumber.
217
 
     */
218
 
    public FastNumber round_down () {
219
 
      FastNumber ret;
220
 
      long decimal = raw_number % PRECISION_FACTOR;
221
 
      if (decimal < 0) {
222
 
        // Is this ever reached?
223
 
        ret = FastNumber.raw (raw_number - PRECISION_FACTOR - decimal);
224
 
      } else {
225
 
        ret = FastNumber.raw (raw_number - decimal);
226
 
      }
227
 
      return ret;
228
 
    }
229
 
    
230
 
    /**
231
 
     * FastNumber to string.
232
 
     * 
233
 
     * @return a string
234
 
     * 
235
 
     * @param decimal whether to return the decimal portion of the number in 
236
 
     *                the string. Default = false.
237
 
     */
238
 
    public string to_string (bool decimal = false) {
239
 
      string ret_val = null;
240
 
      if (!decimal) {
241
 
        ret_val = (this.raw_number / MUL_FACTOR).to_string ();
242
 
      } else {
243
 
        // Copy stuff so we don't accidentality stomp them.
244
 
        long _raw_number = this.raw_number;
245
 
        
246
 
        long _integer_part = (_raw_number / MUL_FACTOR);
247
 
        long _decimal_part = (_raw_number - (_integer_part * MUL_FACTOR));
248
 
        
249
 
        var strbldr = new GLib.StringBuilder ();
250
 
        
251
 
        // normalise the decimal part.
252
 
        // (XXX This is rather expensive, is there a better way of doing this?).
253
 
        if (_decimal_part != 0) {
254
 
          while ((_decimal_part % 10) == 0) {
255
 
            _decimal_part = _decimal_part / 10;
256
 
          }
257
 
        }
258
 
        
259
 
        strbldr.append (_integer_part.to_string ())
260
 
               .append_c ('.');
261
 
        
262
 
        
263
 
        for ( var i = this.leading_zeros ; i > 0 ; i--) {
264
 
          strbldr.append_c ('0');
265
 
        }
266
 
        
267
 
        strbldr.append (_decimal_part.to_string ());
268
 
        
269
 
        ret_val = strbldr.str;
270
 
      }
271
 
      return ret_val;
272
 
    }
273
 
    
274
 
    public double to_float () {
275
 
      // XXX This probobly needs to something faster?
276
 
      return double.parse (this.to_string (true));
277
 
    }
278
 
    
279
 
    public long to_int () {
280
 
       return (this.raw_number / MUL_FACTOR);
281
 
    }
282
 
    
283
 
    /**
284
 
     * Check if two FastNumbers are equal.
285
 
     * 
286
 
     * @return true if this is equal to the other.
287
 
     * @return false if this is not equal to the other.
288
 
     */
289
 
    public bool equals (FastNumber other) {
290
 
      return (this.raw_number == other.raw_number);
291
 
    }
292
 
    
293
 
    
294
 
    [CCode (cname = "vqdr_common_fast_number_compare")]
295
 
    public static extern long static_compare (FastNumber a, FastNumber b);
296
 
    
297
 
    private void parse_raw_number (string str) {
298
 
      //debug (@"(parse_raw_number) str: $str");
 
30
      this.raw_number = parse_raw_number (str);
 
31
    }
 
32
    
 
33
    private static long parse_raw_number (string str) {
299
34
      long ret_val = 0;
300
35
      int i_of_dot = str.index_of_char ('.');
301
36
      if (i_of_dot >= 0) {
 
37
      
302
38
        // Get the decimal number from the string, if such a thing exists.
303
39
        if ((str.length - 1 > i_of_dot)) {
304
 
          var intr_str = (str + "000").substring (i_of_dot + 1);
305
 
          // count leading zeros.
306
 
          long i;
307
 
          for (i = 0; intr_str.@get (i) == '0'; i++){}
308
 
          this.leading_zeros = i;
309
 
          // remove leading zeros
310
 
          intr_str = intr_str.substring (i);
311
 
          //debug (@"(parse_raw_number) Intermediate string: $intr_str");
312
 
          ret_val = long.parse (intr_str);
 
40
          ret_val = long.parse ((str + "000").substring (i_of_dot + 1));
313
41
        }
314
42
        
315
 
        // debug (@"(parse_raw_number) i_of_dot: $i_of_dot, ret_val (decimal): $ret_val\n");
316
 
        
317
43
        // Normalise the digits.
318
44
        while (ret_val > MUL_FACTOR) {
319
45
          ret_val = ret_val / 10;
320
 
          // debug (@"(parse_raw_number) retval (loop): $ret_val");
321
 
        }
322
 
        
323
 
        for (var i = leading_zeros; i > 0; i--) {
324
 
          ret_val = ret_val / 10;
325
 
          // debug (@"(parse_raw_number) retval (loop2): $ret_val");
326
 
        }
327
 
        
328
 
        // debug (@"ret_val (normalised): $ret_val\n");
329
 
        
330
 
        // get integer number
331
 
        ret_val = ret_val + (long.parse (str.substring (0, i_of_dot))
 
46
        }
 
47
        
 
48
        // Add intiger number
 
49
        ret_val = ret_val + (long.parse ("0" + str.substring (0, i_of_dot))
332
50
                            * MUL_FACTOR);
333
 
        
334
51
      } else {
335
 
        ret_val = (long.parse (str) * MUL_FACTOR);
336
 
      }
337
 
      //debug (@"(parse_raw_number) ret_val (finished): $ret_val\n");
338
 
      this.raw_number = ret_val;
339
 
    }
 
52
        ret_val = long.parse (str) * MUL_FACTOR;
 
53
      }
 
54
      return ret_val;
 
55
    }
 
56
    
 
57
    
 
58
    public FastNumber add (FastNumber? other) {
 
59
      if (other == null) {
 
60
        return new FastNumber.copy (this);
 
61
      }
 
62
      
 
63
      var v = new FastNumber (this.raw_number + other.raw_number);
 
64
      
 
65
      return v;
 
66
    }
 
67
    
 
68
    public FastNumber subtract (FastNumber? other) {
 
69
      if (other == null) {
 
70
        return new FastNumber.copy (this);
 
71
      }
 
72
      
 
73
      var v = new FastNumber (this.raw_number - other.raw_number);
 
74
      
 
75
      return v;
 
76
    }
 
77
    
 
78
    public FastNumber multiply (FastNumber? other) {
 
79
      if (other == null || other.raw_value == 0) {
 
80
        return new FastNumber ();
 
81
      }
 
82
      
 
83
      return new FastNumber ((this.raw_number * other.raw_number) / MUL_FACTOR);
 
84
    }
 
85
    
 
86
    public FastNumber divide (FastNumber? other) throws MathError {
 
87
      if (other.raw_number == 0) {
 
88
        throw new MathError.DIVIDE_BY_ZERO
 
89
                                      ("FantNumber - trying to divide by zero");
 
90
      }
 
91
      
 
92
      return new FastNumber ((this.raw_number * MUL_FACTOR) / other.raw_number);
 
93
    }
 
94
    
340
95
  }
 
96
  
341
97
}