-
Notifications
You must be signed in to change notification settings - Fork 0
/
Unsigned32.dm
4307 lines (3294 loc) · 113 KB
/
Unsigned32.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Implementation of an unsigned double precision (32-bit) integer that uses the pif_Arithmetic
* protocol (inherited from the /pif_LongInt superclass). It can accurately store numbers between 0
* and 4,294,967,295 (2**32 - 1) (between 0x00000000 and 0xFFFFFFFF).
*/
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
pif_LongInt/Unsigned32
#else
LongInt/Unsigned32
#endif
/*
* Variables.
*/
var
block_1 = 0 // Least-significant block.
block_2 = 0 // Most-significant block.
const/Length = 2
// FIXED_PRECISION and UNSIGNED_MODE are fixed operation modes that should not be changed. If
// this library is used correctly (e.g., using only the public methods and not using private
// members or directly accessing variables) then the fixed flags can't be changed.
mode = OLD_OBJECT | NO_OVERFLOW_EXCEPTION | FIXED_PRECISION | UNSIGNED_MODE
/*
* Constructor.
*/
New(...)
if(args.len != 0)
var/list/Processed = _ProcessArguments(args)
block_1 = Processed[1]
block_2 = Processed[2]
return src
/*
* Miscellaneous methods.
*/
proc
_ProcessArguments(list/arguments)
// Does the processing for GAAF_format arguments. I'd really prefer this be broken up
// into a number of subroutines, but I think the impact on performance will be too
// significant. If static methods become available in DM, I'll probably break this up
// and then implement methods like /pif_LongInt/FromString() or
// /pif_LongInt/FromIntegers() so that I can avoid a performance hit.
var/list/Data = new
Data.len = Length // Note that Data[1] is less-significant and Data[2] is more-
// significant.
/*
1. proc/Method() or proc/Method(null)
This is a special case that should be interpreted as passing 0.
*/
if(arguments.len == 0)
Data[1] = 0
Data[2] = 0
else if(arguments.len == 1)
if(isnull(arguments[1]))
Data[1] = 0
Data[2] = 0
/*
2. proc/Method(datum/pAr_Object)
i. pAr_Object is an object that implements the pif_Arithmetic protocol, and data
is pulled directly from this object. Ways of handling if pAr_object is too
large for the source object to process are implementation-specific.
If pAr_Object does not appear to implement the pif_Arithmetic protocol (e.g.,
by not having a necessary method) throw a /pif_Arithmetic/ProtocolNonConformingObjectException
exception.
*/
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
else if(istype(arguments[1], /pif_LongInt))
#else
else if(istype(arguments[1], /LongInt))
#endif
// If it's a pif_LongInt object, we can be a little more sure that we're dealing
// with an object that implements the pif_Arithmetic protocol.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
var/pif_LongInt/IntegerObject = arguments[1]
#else
var/LongInt/IntegerObject = arguments[1]
#endif
if((IntegerObject.Length() > Length) && (mode & OVERFLOW_EXCEPTION))
var/L = IntegerObject.Length()
for(var/i = 3, i <= L, i ++)
// We'll look at the other data in the object. If we find any non-zero
// data, then the object is too large to read in and we must throw an
// exception.
if(IntegerObject._GetBlock(i) != 0)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
// Otherwise, just assign the relevant data to the list.
Data[1] = IntegerObject._GetBlock(1)
Data[2] = IntegerObject._GetBlock(2)
else if(istype(arguments[1], /datum))
// If it's some other type of object, we'll assume it's something that
// implements the pif_Arithmetic protocol.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
var/pif_Arithmetic/pif_ArithmeticObject = arguments[1]
#else
var/Arithmetic/pif_ArithmeticObject = arguments[1]
#endif
if(!hascall(pif_ArithmeticObject, "Length") || !hascall(pif_ArithmeticObject, "_GetBlock"))
// If it doesn't conform to the pif_Arithmetic protocol, we throw the
// required exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/ProtocolNonConformingObjectException(__FILE__, __LINE__)
#else
throw new /Arithmetic/ProtocolNonConformingObjectException(__FILE__, __LINE__)
#endif
// Otherwise, we do the same as above.
if((pif_ArithmeticObject.Length() > Length) && (mode & OVERFLOW_EXCEPTION))
// Same as above with the /pif_LongInt object: if we find any non-zero data
// beyond block two, then the object is too large to read in.
var/L = pif_ArithmeticObject.Length()
for(var/i = 3, i <= L, i ++)
if(pif_ArithmeticObject._GetBlock(i) != 0)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
Data[1] = pif_ArithmeticObject._GetBlock(1)
Data[2] = pif_ArithmeticObject._GetBlock(2)
/*
3. proc/Method(list/List)
i. List is a /list object that is interpreted as left-significant. That is, the left
-most entry ("block") of the list (the entry with the lowest index) is
interpreted as the most-significant element of that list, while the right-most
block (the entry with the highest index) is interpreted as the most-significant
element of that list. This is so that something of the form
Method(0x6789, 0xABCD)
is interpreted as the number 0x6789ABCD, which is intuitive. If the list were
interpreted as right-significant, this would instead be the number 0xABCD6789.
All elements of the list should be integers. If a non-integer is found in the
list, then a /pif_Arithmetic/NonIntegerException exception should be thrown. If a
non-numeric value is found then a /pif_Arithmetic/NonNumericInvalidPositionException
exception should be thrown.
If a list contains only one element, then,
a. If SIGNED_MODE is enabled, integers in the range [-16777215, 16777215] must
be supported.
b. If SIGNED_MODE is not enabled, then integers in the range [0, 16777215] must
be supported. If a negative value is found, then either it should be treated
as a raw bitstring or a /pif_Arithmetic/NegativeInvalidPositionException
exception should be thrown.
Interpretation of integers with an absolute value larger than 16777215 is
undefined and left to the implementation.
If the list contains more than one element, then the resulting data should be
treated as raw binary data by performing bitwise AND with the data and the value
0xFFFF. There should be no regard for sign data or floating point values.
*/
else if(istype(arguments[1], /list))
var/list/Args = arguments[1]
if(Args.len == 1)
// When passing a single integer argument, we allow a larger block than
// usual.
var
Integer = Args[1]
block_1
block_2
if(!isnum(Integer))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#endif
else if(round(Integer) != Integer)
// Must be an integer.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
if(Integer < 0)
// If it's less than zero, then we throw the prescribed exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NegativeInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NegativeInvalidPositionException(__FILE__, __LINE__)
#endif
// Any other value we'll interpret as an integer. Only values in the range
// [0, 16777215] are guaranteed to be accurate; above that range, it may not
// be accurate and either strings or lists of two elements should be used.
block_1 = Integer % 65536
block_2 = (Integer - block_1) / 65536
Data[1] = block_1
Data[2] = block_2
else
// Lists of two elements are simply interpreted as bitstrings provided they
// are integer values.
var
block_1 = arguments[arguments.len ]
block_2 = arguments[arguments.len-1]
if(!isnum(block_1) || !isnum(block_2))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#endif
else if( (round(block_1) != block_1) || (round(block_2) != block_2) )
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
// Because this class is unsigned, we will interpret negative values as
// bitstrings.
if(block_1 < 0)
block_1 &= 0xFFFF
if(block_2 < 0)
block_2 &= 0xFFFF
Data[1] = block_1
Data[2] = block_2
for(var/i = 1, i <= Args.len-2, i ++)
// Now we look through the rest of the arguments to make sure they don't
// violate any requirements of the GAAF format.
var/block = Args[i]
if(!isnum(block))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#endif
else if(round(block) != block)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
else if((mode & OVERFLOW_EXCEPTION) && (block != 0))
// If there is non-zero data and OVERFLOW_EXCEPTION mode is enabled,
// then the incoming data is too large to read in and we must throw
// the exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
/*
4. proc/Method(String)
i. String is a string with the following requirement.
a. If unprefixed (e.g., "12345") the string is interpreted as a base ten
(decimal) number using the characters in the set {"0", "1", "2", "3", "4",
"5", "6", "7", "8", "9"} with their standard decimal interpretation. If a
character other than these is found, then a /pif_Arithmetic/InvalidStringEncodingException
exception is thrown.
b. If previxed with "0x" (e.g., "0x1234") the string is interpreted as a base
sixteen (hexadecimal) number using the characters in the set {"0", "1", "2",
"3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "a", "b",
"c", "d", "e", "f"}, using their standard hexadecimal interpretation;
furthermore, mixed case (i.e., both upper and lower case) is allowed. If a
character other than these is found beyond the prefix, then a
/pif_Arithmetic/InvalidStringEncodingException exception is thrown.
c. If prefixed with "0b" (e.g., "0b1010") the string is interpreted as a base two
(binary) number using the characters in teh set {"0", "1"} using their
standard binary interpretation. If a character other than these is found
beyond the prefix, then a /pif_Arithmetic/InvalidStringEncodingException
exception is thrown.
To indicate a negative number, a negative sign appears before the prefix for
binary and hexadecmal number (e.g., "-0b1010" or "-0x1234") and as usual for
decimal numbers (e.g., "-150"). If a negative sign is found in a number with
SIGNED_MODE disabled, then a /pif_Arithmetic/NegativeInvalidPositionException
exception is thrown.
If an invalid prefix specifically is found, then a /pif_Arithmetic/InvalidStringPrefixException
exception is thrown. If String is a zero-length string (i.e., "") then a
/pif_Arithmetic/InvalidStringArgumentException exception is thrown.
*/
else if(istext(arguments[1]))
var/String = arguments[1]
if(length(String) == 0)
// Zero-length strings are not allowed.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/InvalidStringArgumentException(__FILE__, __LINE__)
#else
throw new /Arithmetic/InvalidStringArgumentException(__FILE__, __LINE__)
#endif
if(findtext(String, "-", 1, 2))
// Strings encoding negative numbers are not allowed in unsigned integers.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NegativeInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NegativeInvalidPositionException(__FILE__, __LINE__)
#endif
if(findtextEx(String, "0b", 1, 3))
/*
* We have a string with a binary-encoded number.
*/
var
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
pif_LongInt/Unsigned32/Tracker = new src.type
#else
LongInt/Unsigned32/Tracker = new src.type
#endif
length = length(String) - 2 // Subtracting two because of the prefix.
const/ASCII_ZERO = 48
Tracker.SetModeFlag(NEW_OBJECT, 0)
for(var/i = length, i > 0, i --)
// This loop starts from the *end* of the string and moves forward, so
// we can get the least-significant characters first. This is largely
// because it makes the math a bit easier.
var/c = text2ascii(String, i+2) - ASCII_ZERO
if( (c != 0) && (c != 1) )
// If we've encountered an invalid character, throw an exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/InvalidStringEncodingException(__FILE__, __LINE__)
#else
throw new /Arithmetic/InvalidStringEncodingException(__FILE__, __LINE__)
#endif
if((length - i) < 16)
Tracker.Add(0x0000, c << (length - i))
else if((length - i) < 32)
Tracker.Add(c << ((length - i) - 16), 0x0000)
else
if(!(mode & OVERFLOW_EXCEPTION))
// If we're at a point larger than can be stored in a double
// precision integer, and if we're aren't tracking for overflow,
// then just stop the loop.
break
else if(c != 0)
// Otherwise, wait until we find a non-zero term and throw the
// exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
Data[1] = Tracker._GetBlock(1)
Data[2] = Tracker._GetBlock(2)
else if(findtextEx(String, "0x", 1, 3))
/*
* A string with a hexadecimal-encoded integer.
*/
var
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
pif_LongInt/Unsigned32/Tracker = new src.type
#else
LongInt/Unsigned32/Tracker = new src.type
#endif
length = length(String) - 2 // Subtracting two because of the prefix.
const
HEX_ZERO = 0x0000
HEX_NINE = 0x0009
HEX_FIFTEEN = 0x000F
ASCII_ZERO = 48
ASCII_NINE = 57
ASCII_DIFF = 39
LOWERCASE_BIT = 32 // Turning this bit on will convert an uppercase
// character to lowercase.
Tracker.SetModeFlag(NEW_OBJECT, 0)
for(var/i = length, i > 0, i --)
// This loop starts from the *end* of the string and moves forward, so
// we can get the least-significant characters first. This is largely
// because it makes the math a bit easier.
var/c = text2ascii(String, i+2)
if(c > ASCII_NINE)
// If it's non-numeric, then make it lowercase so we can have
// consistent behavior.
c |= LOWERCASE_BIT
// We subtract the value of ASCII_ZERO so that "0" maps to 0, "1" maps
// to 1, ..., and "9" maps to 9...
c -= ASCII_ZERO
if(c > HEX_NINE)
// ... but, "a", ..., "f" will map to 49, ..., 54, so we have to
// subtract the correct difference (ASCII_DIFF) to have them map to
// 10, ..., 15.
c -= ASCII_DIFF
if( (c < HEX_ZERO) || (c > HEX_FIFTEEN) )
// If we've encountered an invalid character, throw an exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/InvalidStringEncodingException(__FILE__, __LINE__)
#else
throw new /Arithmetic/InvalidStringEncodingException(__FILE__, __LINE__)
#endif
if((length - i) < 4)
// If i is in the range [0, 4) then we can still write to the first
// block.
Tracker.Add(0x0000, c << 4*(length - i))
else if((length - i) < 8)
// If i is in the range [4, 8) then we can still write to the second
// block.
Tracker.Add(c << 4*((length - i) - 4), 0x0000)
else
// If i is 8 or more, then there are no more blocks to write to.
if(!(mode & OVERFLOW_EXCEPTION))
// If we're at a point larger than can be stored in a double
// precision integer, and if we're aren't tracking for overflow,
// then just stop the loop.
break
else if(c != HEX_ZERO)
// Otherwise, wait until we find a non-zero term and throw the
// exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
Data[1] = Tracker._GetBlock(1)
Data[2] = Tracker._GetBlock(2)
else if(findtext(String, regex("0\[^0-9]"), 1, 3))
// We found another prefix but one that is unsupported.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/InvalidStringPrefixException(__FILE__, __LINE__)
#else
throw new /Arithmetic/InvalidStringPrefixException(__FILE__, __LINE__)
#endif
else
// If only the character 0-9 show up in the string, then it's a valid
// decimal string and we may proceed.
// Anything else and we assume a decimal-encoded string was passed. This
// method seems a bit slow, so I'd love to figure out a faster version.
var
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
pif_LongInt/Unsigned32
#else
LongInt/Unsigned32
#endif
// This tracks the current position. That is, at each step we
// multiply it by ten to keep it in the right position.
PositionTracker = new src.type(1)
Buffer = new src.type // Temporarily holds a product before adding
// it to the Tracker object.
Tracker = new src.type // Tracks the final value.
length = length(String)
const/ASCII_ZERO = 48
PositionTracker.SetModeFlag(OVERFLOW_EXCEPTION, mode & OVERFLOW_EXCEPTION)
PositionTracker.SetModeFlag(NEW_OBJECT, 0)
Tracker.SetModeFlag(NEW_OBJECT, 0)
Buffer.SetModeFlag(NEW_OBJECT, 0)
for(var/i = length, i > 0, i --)
// This loop starts from the *end* of the string and moves forward, so we
// can get the least-significant characters first. This is largely because
// it makes the math a bit easier.
var/c = text2ascii(String, i) - ASCII_ZERO
if((c < 0) || (c > 9))
// If we've encountered an invalid character, throw an exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/InvalidStringEncodingException(__FILE__, __LINE__)
#else
throw new /Arithmetic/InvalidStringEncodingException(__FILE__, __LINE__)
#endif
else if(c != 0)
// If c is non-zero, then compute the corresponding value, store it
// in the buffer, and add it to the tracker.
// If c is non-zero, then we store the value in the buffer, multiply
// the buffer by the current position as stored in PositionTracker,
// and then add the result to the Tracker object.
Buffer.Set(c)
Tracker.Add(Buffer.Multiply(PositionTracker))
PositionTracker.Multiply(10)
Data[1] = Tracker._GetBlock(1)
Data[2] = Tracker._GetBlock(2)
/*
7. proc/Method(...)
A left-significant list of integer arguments. See 3. for details on this format.
*/
else if(isnum(arguments[1]))
// This situation is when a single integer has been passed as an argument.
var
Integer = arguments[1]
block_1
block_2
if(!isnum(Integer))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#endif
else if(round(Integer) != Integer)
// Must be an integer.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
if(Integer < 0)
// If it's less than zero, then we throw the prescribed exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NegativeInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NegativeInvalidPositionException(__FILE__, __LINE__)
#endif
// Any other value we'll interpret as an integer. Only values in the range [0,
// 16777215] are guaranteed to be accurate; above that range, it may not be
// accurate and either strings or lists of two elements should be used.
block_1 = Integer % 65536
block_2 = (Integer - block_1) / 65536
Data[1] = block_1
Data[2] = block_2
/*
If the format provided for a GAAF-specified method does not match one of the above
formats, then a /pif_Arithmetic/InvalidArgumentFormatException exception is thrown.
*/
else
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/InvalidArgumentFormatException(__FILE__, __LINE__)
#else
throw new /Arithmetic/InvalidArgumentFormatException(__FILE__, __LINE__)
#endif
/*
TODO:
5. proc/Method(String, Base, EncodingRef)
i. String is an arbitrary string. If this is argument is not a string or is a zero-
length string (i.e., "") then a /pif_Arithmetic/InvalidStringArgumentException
exception is thrown.
ii. Base is a positive integer indicating the base the string. If Base does not
satisfy these requirements (e.g. Base is a non-integer, or is zero or negative)
then a /pif_Arithmetic/InvalidStringBaseException exception is thrown.
iii. EncodingRef is a proc typepath (e.g., /proc/MyEncodingProc) that accepts a single
character from the string and outputs a positive integer that indicates the value
of that character. If the returned value is not a positive integer, a
/pif_Arithmetic/InvalidStringEncodingValueException exception is thrown. If
EncodingRef accepts an invalid character, a /pif_Arithmetic/InvalidStringEncodingException
exception is thrown by the EncodingRef proc.
6. proc/Method(String, Base, datum/EncodingObj, EncodingRef)
i. String is an arbitrary string. If this is argument is not a string or is a zero-
length string (i.e., "") then a /pif_Arithmetic/InvalidStringArgumentException
exception is thrown.
ii. Base is a positive integer indicating the base the string. If Base does not
satisfy these requirements (e.g. Base is a non-integer, or is zero or negative)
then a /pif_Arithmetic/InvalidStringBaseException exception is thrown.
iii. EncodingObj is an object that EncodingRef is attached to. If this argument is not
an object, then a /pif_Arithmetic/InvalidStringEncodingObjException exception is
thrown.
iv. EncodingRef is a string (e.g., "MyEncodingMethod") that is the name of a method
on the EncodingObj object. This omethod accepts a single character from the
string and outputs a positive integer that indicates the value of that character.
If the returned value is not a positive integer, a /pif_Arithmetic/InvalidStringEncodingValueException
exception is thrown. If EncodingRef accepts an invalid character, a
/pif_Arithmetic/InvalidStringEncodingException exception is thrown by the
EncodingRef method.
*/
// ...
/*
7. proc/Method(...)
A left-significant list of integer arguments. See 3. for details on this format.
*/
else
var
block_1 = arguments[arguments.len ]
block_2 = arguments[arguments.len-1]
if(!isnum(block_1) || !isnum(block_2))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#endif
else if( (round(block_1) != block_1) || (round(block_2) != block_2) )
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
// Because this class is unsigned, we will interpret negative values as bitstrings.
if(block_1 < 0)
block_1 &= 0xFFFF
if(block_2 < 0)
block_2 &= 0xFFFF
Data[1] = block_1
Data[2] = block_2
for(var/i = 1, i <= arguments.len-2, i ++)
// Now we look through the rest of the arguments to make sure they don't violate
// any requirements of the GAAF format.
var/block = arguments[i]
if(!isnum(block))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonNumericInvalidPositionException(__FILE__, __LINE__)
#endif
else if(round(block) != block)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
else if((mode & OVERFLOW_EXCEPTION) && (block != 0))
// If there is non-zero data and OVERFLOW_EXCEPTION mode is enabled, then
// the incoming data is too large to read in and we must throw the
// exception.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
return Data
ToFloat()
// Outputs the value of the data as a float. Because floating point numbers are only
// approximately accurate, this may not be the "true" value of the integer. As such,
// it's possible that converting to a float and back to an integer will result in a
// different value than was there originally.
return ((block_2 & 0x8000) >> 15) * 2147483648 + \
((block_2 & 0x4000) >> 14) * 1073741824 + \
((block_2 & 0x2000) >> 13) * 536870912 + \
((block_2 & 0x1000) >> 12) * 268435456 + \
((block_2 & 0x0800) >> 11) * 134217728 + \
((block_2 & 0x0400) >> 10) * 67108864 + \
((block_2 & 0x0200) >> 9) * 33554432 + \
((block_2 & 0x0100) >> 8) * 16777216 + \
((block_2 & 0x0080) >> 7) * 8388608 + \
((block_2 & 0x0040) >> 6) * 4194304 + \
((block_2 & 0x0020) >> 5) * 2097152 + \
((block_2 & 0x0010) >> 4) * 1048576 + \
((block_2 & 0x0008) >> 3) * 524288 + \
((block_2 & 0x0004) >> 2) * 262144 + \
((block_2 & 0x0002) >> 1) * 131072 + \
(block_2 & 0x0001) * 65536 + \
block_1
/*
* "Private" methods.
*/
_GetBlock(i)
if(!isnum(i))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
if(i <= 0)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonPositiveIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonPositiveIntegerException(__FILE__, __LINE__)
#endif
if(i > Length)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OutOfBoundsException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OutOfBoundsException(__FILE__, __LINE__)
#endif
return (i == 1) ? block_1 : block_2
_SetBlock(i, j)
if(!isnum(i) || !isnum(j))
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonIntegerException(__FILE__, __LINE__)
#endif
if(i <= 0)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/NonPositiveIntegerException(__FILE__, __LINE__)
#else
throw new /Arithmetic/NonPositiveIntegerException(__FILE__, __LINE__)
#endif
if(i > Length)
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OutOfBoundsException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OutOfBoundsException(__FILE__, __LINE__)
#endif
if(i == 1)
block_1 = j
return block_1
else
block_2 = j
return block_2
/*
* Arithmetic methods.
*/
Add(...)
/*
* Argument processing and assignment of variables.
*/
var
list/Processed = _ProcessArguments(args)
Int_block_1 = Processed[1] // Least significant.
Int_block_2 = Processed[2] // Most significant.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
pif_LongInt/Unsigned32/Sum
#else
LongInt/Unsigned32/Sum
#endif
if(mode & NEW_OBJECT)
Sum = new src.type(src)
else
Sum = src
/*
* Now we do the actual computation.
*/
var
// Used to store the result of addition before being sent to the Sum object. These are
// the first and second bytes of a given block, respectively.
B1 = 0
B2 = 0
// Compute the first block.
B1 = pliBYTE_ONE(block_1) + pliBYTE_ONE(Int_block_1)
B2 = pliBYTE_TWO(block_1) + pliBYTE_TWO(Int_block_1) + pliBUFFER(B1)
Sum._SetBlock(1, pliBYTE_ONE(B1) | pliBYTE_ONE_SHIFTED(B2))
// And the second block.
B1 = pliBYTE_ONE(block_2) + pliBYTE_ONE(Int_block_2) + pliBUFFER(B2)
B2 = pliBYTE_TWO(block_2) + pliBYTE_TWO(Int_block_2) + pliBUFFER(B1)
Sum._SetBlock(2, pliBYTE_ONE(B1) | pliBYTE_ONE_SHIFTED(B2))
if( (pliBUFFER(B2) != 0) && (mode & OVERFLOW_EXCEPTION) )
// If B2's buffer is not equal to zero, then we overflowed and need to throw the
// OverflowException if it's flag set.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
return Sum
Subtract(...)
/*
* Argument processing and assignment of variables.
*/
var
list/Processed = _ProcessArguments(args)
Int_block_1 = Processed[1] // Least significant.
Int_block_2 = Processed[2] // Most significant.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
pif_LongInt/Unsigned32/Diff
#else
LongInt/Unsigned32/Diff
#endif
if(mode & NEW_OBJECT)
Diff = new src.type(src)
else
Diff = src
/*
* Now we do the actual computation.
*/
var
// Used to store the result of addition before being sent to the Sum object. These are
// the first and second bytes of a given block, respectively.
B1 = 0
B2 = 0
// Compute the first block. The computation is largely the same as in addition, except that
// for each step we take the bitwise not of the relevant Int block, as well as adding 1 to
// the first part of Int_block_1. In two's complement notation, this is equivalent to
// negating it.
B1 = pliBYTE_ONE(block_1) + pliBYTE_ONE_N(Int_block_1) + 1 // Adding one for negation.
B2 = pliBYTE_TWO(block_1) + pliBYTE_TWO_N(Int_block_1) + pliBUFFER(B1)
Diff._SetBlock(1, pliBYTE_ONE(B1) | pliBYTE_ONE_SHIFTED(B2))
// And now the second block.
B1 = pliBYTE_ONE(block_2) + pliBYTE_ONE_N(Int_block_2) + pliBUFFER(B2)
B2 = pliBYTE_TWO(block_2) + pliBYTE_TWO_N(Int_block_2) + pliBUFFER(B1)
Diff._SetBlock(2, pliBYTE_ONE(B1) | pliBYTE_ONE_SHIFTED(B2))
if( (pliBUFFER(B2) == 0) && (mode & OVERFLOW_EXCEPTION) )
// If B2's buffer is equal to zero, then there was a negative overflow. Thus, we need to
// possibly throw the OverflowException.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_ARITHMETIC)
throw new /pif_Arithmetic/OverflowException(__FILE__, __LINE__)
#else
throw new /Arithmetic/OverflowException(__FILE__, __LINE__)
#endif
return Diff
Multiply(...)
var
list/Processed = _ProcessArguments(args)
Int_block_1 = Processed[1] // Least significant.
Int_block_2 = Processed[2] // Most significant.
#if !defined(PIF_NOPREFIX_GENERAL) && !defined(PIF_NOPREFIX_LONGINT)
pif_LongInt/Unsigned32/Prod
#else
LongInt/Unsigned32/Prod
#endif
if(mode & NEW_OBJECT)
Prod = new src.type(null)
else
Prod = src
/*
* Computation of multiplication.
*
The idea beihind the following method is this: Let A and B be two integers between 0 and
2**32 - 1. Then if R = 256, we may write them each as
A = R**3 a_3 + R**2 a_2 + R a_1 + a_0
B = R**3 b_3 + R**2 b_2 + R b_1 + b_0
where a_n and b_n are between 0 and 255 (for n = 0, 1, 2, 3). If we take the product A*B of
A and B, then rearrange the terms so that terms with the same power of R as coefficients are
grouped together, we then get
A*B = R**6 (a_3*b_3) + R**5 (a_3*b_2 + a_2*b_3) + R**4 (a_3*b_1 + a_2*b_2 + a_1*b_3) +
R**3 (a_3*b_0 + a_2*b_1 + a_1*b_2 + a_0*b_3) + R**2 (a_2*b_0 + a_1*b_1 + a_0*b_1) +
R (a_1*c_0 + a_0*c_1) + a_0*b_0.
Then note that R**4 = 256**4 = (2**8)**4 = 2**32. But, 2**32 > 2**32 - 1, so each term with
a coefficienet larger than R**3 will be larger than 2**32 - 1 and too large to store in this
object. Therefore, they are simply discarded.
Instead, we will only compute the terms whose coefficients are R**3, R**2, R**1 == R, or
R**0 == 1. We'll do a quick check for the others if we must check for overflow.
*/
var
// These are the bytes of both src and Int.
src0 = pliBYTE_ONE(block_1) // Least significant.
src1 = pliBYTE_TWO(block_1)
src2 = pliBYTE_ONE(block_2)
src3 = pliBYTE_TWO(block_2) // Most significant.
Int0 = pliBYTE_ONE(Int_block_1) // Least significant.
Int1 = pliBYTE_TWO(Int_block_1)
Int2 = pliBYTE_ONE(Int_block_2)