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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
|
"""bps.numeric -- numeric extensions for bps3.
this is mainly an extension of the 'math' library.
"""
#=========================================================
#imports
#=========================================================
#core
from __builtin__ import pow as intpow
import sys
from decimal import Decimal
from itertools import izip
trunc = int #clobbered by real implementation under py26+
from math import * #NOTE: we import everything from math so this module can act as a drop-in replacement
#pkg
from bps.meta import isnum, isseq
from bps.rng import random, srandom
#local
__all__ = [
#numeric format conversions
'int_to_base',
## 'base_to_int',
'float_to_base',
## 'base_to_float',
'int_to_roman',
'roman_to_int',
#byte strings
'int_to_bytes', 'bytes_to_int',
'list_to_bytes', 'bytes_to_list',
'xor_bytes', 'and_bytes', 'or_bytes', 'neg_bytes', 'binop_bytes',
#numthy
'gcd', 'lcm', 'factors',
#prime
'is_prime', 'get_factors',
'next_prime', 'prev_prime', 'iter_primes',
#misc
'sdivmod', 'splitfrac',
'limit', 'avgsd', 'digits',
]
#calc bits-per-float... (usually 53)
BPF = 0
while (1+2**-BPF) > 1:
BPF += 1
EMPTY_BYTES = ""
#=========================================================
#misc
#=========================================================
def sdivmod(x,y):
"""divmod variant which preserves sign of remainder"""
d,r = divmod(x,y)
if x < 0 and r > 0: #NOTE: r < 0 if x is negative Decimal instance
d += 1
r -= y
return d,r
def splitfrac(v):
"split number into integer portion and fractional portion; returns ``(int_part as int, frac_part as original type)``"
#NOTE: this reason this is present instead of various other solutions:
# modf(v) - always returns (float,float); whereas it's frequently needed for int part to be integer.
# also, modf coerces Decimal to float, this preserves Decimal in the fractional portion
# divmod(v,1) or v%1 - doesn't handle negative values correctly, and int part is not integer
# sdivmod(v,1) - int part is not integer, and too more complex for common case
if isinstance(v, (int,long)):
return v, 0
else:
ip = trunc(v)
return ip, v - ip
def limit(value, lower, upper):
"""constraints value to specified range.
:arg value: value to clamp
:arg lower: smallest value allowed
:arg upper: largest value allowed
:returns:
value, if it's between lower & upper.
otherwise returns the appropriate limit;
Usage Example::
>>> from bps.numeric import limit
>>> limit(-1,0,1)
0
>>> limit(.5,0,1)
.5
>>> limit(100,0,1)
1
"""
if lower > upper:
raise ValueError, "lower must be <= upper"
if value < lower:
return lower
if value > upper:
return upper
return value
def digits(value, base=10):
"""Returns minimum number of digits required to represent value under a given base.
:arg value: integer value to check.
:arg base: base to count the digits for (defaults to base 10).
:returns:
Returns minimum number of digits needed under specified base.
Negative numbers will be converted to positive.
``0`` is special-cased to always return ``1``.
Thus this will always return a value >= 1.
Usage Example::
>>> from bps.numeric import digits
>>> digits(99,10)
2
>>> digits(100,10)
3
>>> digits(7,2)
3
>>> digits(8,2)
4
>>> digits(255,16)
2
"""
if value == 0:
return 1
if value < 0: #ignore the minus sign
value = -value
return int(ceil(log(value+1, base)))
def avgsd(args, sample=False):
"calc avg & std deviation of a sequence of numbers"
if not hasattr(args, "__len__"):
args = list(iter(args))
if not args:
raise IndexError, "empty list passed in"
num = len(args)
avg = sum(args) / float(num)
if sample and num > 1:
num -= 1
sigma = sqrt(sum((x - avg)**2 for x in args) / num)
return avg, sigma
#===================================================
#number theory functions
#===================================================
def gcd(a, b):
"""returns the greatest common divisor of the integers *a* and *b*."""
if b < 0:
b = -b
while b:
a, b = b, (a % b)
return a
def lcm(a, b):
"""returns least common multiple of the integers *a* and *b*"""
# lcm = abs(a*b) / gcd(a,b)
if a == 0 and b == 0:
return 0
ab = a*b
if ab < 0:
ab = -ab
g = gcd(a, b)
assert ab % g == 0
return ab//g
def factors(value):
"""return list of factor of integer *value*
:arg value:
The integer to factor.
This can be negative, 0, or positive.
:returns:
This will return a list of prime & exponent pairs.
For example, ``factors(48)`` would return ``[(2,4),(3,1)])``,
signifying that ``(2**4) * (3**1) == 48``.
Invariants:
* All factors will be listed in ascending order,
* All exponents will be > 0.
Special Cases:
* If the value is prime, a single entry will be returned,
being ``[(value,1)]``.
* Negative values will be made positive first.
* The numbers 0 and 1 will return empty lists.
Usage Example::
>>> from bps.numeric import factors
>>> #factors for a prime are just itself
>>> factors(5) # 5**1
[(5, 1)]
>>> #factors for a larger number...
>>> factors(104) # 2**3 * 13**1
[(2, 3), (13, 1)]
>>> #factors for a negative act just like the positive
>>> factors(-10)
[(2, 1), (5, 1)]
"""
#TODO: find more efficient factoring algorithm
if value < 0:
value = -value
if value < 2:
return []
#check if prime (should be quick)
if is_prime(value):
return [ (value, 1) ]
#pull off prime factors as we find them
out = []
for prime in iter_primes():
count = 0
while value % prime == 0:
value //= prime
count += 1
if count:
out.append((prime, count))
if value == 1:
return out
#===================================================
#prime iteration
#===================================================
#the first 64 primes, for quick testing.
_small_primes = [
2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,
137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,
227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,
]
#---------------------------------------------------
#primality testing
#---------------------------------------------------
def is_mr_prime(value, rounds=None):
"""tests for primality using the miller-rabin test.
:arg value:
The value to test for primality
:param rounds:
The number of rounds to use.
The chances of a false positive are ``1/(4**rounds)``.
By default, the number of rounds will be chosen
such that the chances of a false positive are ``1/(value**2)``,
so the larger the prime, the more certain you can be :)
:returns:
``False`` if the number is confirmed composite.
``True`` if the number is probably prime (w/in bounds
set by number of rounds).
"""
#boundary cases
if value < 2:
return False
elif value == 2 or value == 3:
return True
elif value & 1 == 0:
return False
#determine number of rounds
if rounds is None:
#pick default number of rounds based on size of value.
#add 2 rounds for every bit in number...
#since prob of false positive is 4**(-rounds),
#prob of false positive for n is 4**(-log(n,2)),
#or (n**-2).
rounds=max(256, int(log(value,2)))
## max_rounds = value//4+1
## if rounds > max_rounds:
## rounds = max_rounds
if rounds >= value-10:
#run through all witnesses [2..value-1]
rounds = value-2
rounds_mode = 1
else:
#randomly pick witnesses
rounds_mode = 0
#find positive ints s & d s.t.
# (2**s)*d == value-1, with d % 2 == 1
vone = value-1
assert vone & 1 == 0
s = 0
d = vone
while d & 1 == 0:
d >>= 1
s += 1
assert d & 1
assert s > 0
assert (1<<s)*d == vone
r_range = xrange(1, s)
#test some candidates
#TODO: prevent repetitions? deal w/ small modulus?
randrange = random.randrange
for i in xrange(rounds):
#generate random candidate
if rounds_mode == 0:
a = randrange(2, vone)
else:
assert rounds_mode == 1
a = i+2
#check if a**d = 1 or -1 mod value
x = intpow(a, d, value)
if x == 1 or x == vone:
#satisfied condition for primality witness,
#it's not a composite witness
#try another one
continue
## #just to save some speed, run fermat primality test first
## # if a**vone is not 1, then a**(d*2**r) will never be 1 OR -1 for any r,
## # so there's no point in doing mr loop, and intpow is cheaper
## if intpow(a, vone, value) != 1:
## return False
## #...so now we only let primes & carmichael numbers through
#check if a**(2**r*d) for 0<=r<=s-1, but above check already done for r=0
for r in r_range:
x = intpow(x, 2, value)
if x == 1:
#no chance it'll ever be -1, it's a composite witness
return False
if x == vone:
#satisfied condition for primality witness,
#it's not a composite witness
#try another one
break #break 'r' loop; continue 'i' loop
else:
#no chance it'll ever be -1, it's a composite witness
return False
#probably prime, change we're wrong is 1/4**rounds
return True
def is_prime(value):
"""Test if integer is prime.
.. note::
Implementation wise, this does a quick check
against some smaller primes, and then falls
back to the miller-rabin test.
"""
if value < 2:
return False
#do quick check for the small ones
for prime in _small_primes:
if value % prime == 0:
return value == prime
#anything up to square of last prime in list has to be prime
if value <= prime*prime:
return True
#fallback to miller-rabin
return is_mr_prime(value)
#---------------------------------------------------
#prime iteration
#---------------------------------------------------
###don't use, not cryptographically useful
##def rand_prime(bits, is_prime=is_prime, rng=srandom):
## """generate a random prime.
##
## :param bits:
## the minimum number of bits,
## s.t. ``log(prime,2) >= bits``
##
## """
## rng.reseed()
## #generate odd number between 1<<(bits-1) and 1<<bits
## cur = (rng.getrandbits(bits-1)<<1) + 1
## while True:
## if is_prime(cur):
## return cur
## cur += 2
def iter_primes(start=0, stop=None, count=None, rounds=None):
"""generator which returns sequential primes.
:param start:
Starting point for generating primes.
The first value generates will be the smallest
prime which is greater than or equal to *start*.
This defaults to ``0``, which means
all primes starting with ``2`` will be genereated in order.
:param stop:
If specified, the generator will stop
after yeilding the largested prime which is
strictly less than *stop*.
:param count:
If specified, the generator will stop
after yielding *count* prime numbers.
If a *stop* is also specified,
the generator will halt on whichever
condition is reached first.
:param rounds:
Optionally lock the number of rounds
used by the Miller-Rabin test.
This is generally not needed.
:returns:
A generator which will yield ascending prime numbers
according to the parameters above.
"""
if stop or count:
#wrap ourselves with a counter
idx = 0
for prime in iter_primes(start, rounds=rounds):
if stop and prime >= stop:
return
yield prime
idx += 1
if count and idx >= count:
return
#yield from small primes list to get things started
global _small_primes
top = _small_primes[-1]
if start <= top:
for prime in _small_primes:
if prime < start:
continue
yield prime
cur = prime+2
else:
cur = start|1
#iterate one by one, using modified is_prime() to test
assert cur > top
assert cur & 1 == 1
while True:
for prime in _small_primes:
if cur % prime == 0:
break
else:
if is_mr_prime(cur, rounds=rounds):
yield cur
cur += 2
def next_prime(value, rounds=None):
"return the smallest prime strictly greater than the specified value"
#pick from small primes list
top = _small_primes[-1]
if value <= top:
for prime in _small_primes:
if prime <= value:
continue
return prime
value = prime+2
elif value & 1:
value += 2
else:
value += 1
#iteratoe one by one, using modified is_prime to test
assert value > top
assert value & 1
while True:
for prime in _small_primes:
if value % prime == 0:
break
else:
if is_mr_prime(value, rounds=rounds):
return value
value += 2
def prev_prime(value, rounds=None):
"return the largest prime strictly less than the specified value, or ``None``"
top = _small_primes[-1]
#pick from small primes list
if value <= top:
for prime in reversed(_small_primes):
if prime >= value:
continue
return prime
assert value <= 2
return None
#step value down to next candidate
if value & 1:
value -= 2
else:
value -= 1
assert value & 1
assert value >= top
#iteratoe one by one, until we reach top of preset list
while value > top:
for prime in _small_primes:
if value % prime == 0:
break
else:
if is_mr_prime(value, rounds=rounds):
return value
value -= 2
return top
#=========================================================
#int <-> binary encoded string
#=========================================================
def _log256_ceil(value):
"helper for mchr & mord"
#FIXME: probably a nice clever way to do this w/ integer / bitshifting
if value < 0:
return 0
return int(ceil(log(value, 256)))
def int_to_bytes(num, bytes=None, upper=None, order="big"):
"""Returns a multi-character string corresponding to the ordinal *num*.
Bit String Encoding:
This is the multi-character equivalent of :func:`chr`, with some additional features.
It takes in a positive integer, and returns a string representation,
packed into a specified number of bytes.
:arg num:
The positive integer to encode.
:param bytes:
Optionally, the number of bytes to encode integer into.
If specified, this will be the length of the returned string.
If not specified, this will default to the minimum number
required to encode the number.
:param upper:
Upper bound allowed for the number.
If not specified, but *bytes* is, upper will default
to the largest number representable by that number of bytes.
If num is equal to or larger than upper, a ValueError will be raised.
:param order:
Byte ordering: "big", "little", "native".
The default is "big", since this the common network ordering,
and "native" as the default would present poor cross-platform predictability.
:returns:
The number encoded into a string, according to the options.
Usage Example::
>>> from bps.numeric import bytes_to_int, int_to_bytes
>>> int_to_bytes(1234, bytes=4)
'\\x00\\x00\\x04\\xd2'
>>> int_to_bytes(1234, bytes=4, order="little")
'\\xd2\\x04\\x00\\x00'
>>> bytes_to_int('\\x00\\x00\\x04\\xd2')
1234
"""
#TODO: would a "bits" keyword be useful?
if bytes is not None:
#encode in specified number of bytes
if bytes < 1:
raise ValueError, "bytes must be None or >= 1: %r" % (bytes,)
bupper = 256**bytes
if upper is None:
upper = bupper
elif upper > bupper:
raise ValueError, "upper bound too large for number of bytes: bytes=%r upper=%r" % (bytes, upper)
else:
if upper is None:
upper = num+1
bytes = _log256_ceil(upper)
if upper < 0:
raise ValueError, "top must be >= 0: %r" % (upper,)
if num < 0 or num >= upper:
raise ValueError, "expected number between 0 and %d: %d" % (upper-1, num)
if order == "native":
order = sys.byteorder
if order == "big": #encode bytes-1 byte first, 0 byte last
itr = xrange(bytes*8-8, -8, -8)
else: #encode 0 byte first, bytes-1 byte last
assert order == "little"
itr = xrange(0, bytes*8, 8)
return EMPTY_BYTES.join(
chr((num >> offset) & 255)
for offset in itr
)
def list_to_bytes(value, bytes=None, order="big"):
"""Returns a multi-character string corresponding to a list of byte values.
This is similar to :func:`int_to_bytes`, except that this a list of integers
instead of a single encoded integer.
:arg value:
The list of integers to encode.
It must be true that ``all(elem in range(0,256)) for elem in value``,
or a ValueError will be raised.
:param bytes:
Optionally, the number of bytes to encode to.
If specified, this will be the length of the returned string.
:param order:
Byte ordering: "big", "little", "native".
The default is "big", since this the common network ordering,
and "native" as the default would present poor cross-platform predictability.
:returns:
The number encoded into a string, according to the options.
Usage Example::
>>> from bps.numeric import list_to_bytes, bytes_to_list
>>> list_to_bytes([4, 210], 4)
'\\x00\\x00\\x04\\xd2'
>>> list_to_bytes([4, 210], 4, order="little")
'\\xd2\\x04\\x00\\x00'
>>> bytes_to_list('\\x00\\x00\\x04\\xd2')
[4, 210]
"""
#make sure all elements have valid values
if any( elem < 0 or elem > 255 for elem in value):
raise ValueError, "value must be list of integers in range(0,256): %r" % (value,)
#validate bytes / upper
if bytes is None:
bytes = len(value)
if bytes == 0:
raise ValueError, "empty list not allowed"
else:
if bytes < 1:
raise ValueError, "bytes must be None or >= 1: %r" % (bytes,)
if len(value) > bytes:
raise ValueError, "list too large for number of bytes: bytes=%r len=%r" % (bytes, len(value))
#encode list in big endian mode
out = ''.join( chr(elem) for elem in value )
pad = bytes-len(out)
#pad/reverse as needed for endianess
if order == "native":
order = sys.byteorder
if order == "big":
if pad:
out = ('\x00' * pad) + out
else:
assert order == "little"
if pad:
out = out[::-1] + ('\x00' * pad)
else:
out = out[::-1]
return out
def bytes_to_int(value, order="big"):
"""decode a string into an integer representation of it's binary values.
Bit String Decoding:
This returns a positive integer, as decoded from the string.
This is the inverse of the :func:`int_to_bytes` function.
:arg value:
The string to decode.
:param order:
The byte ordering, defaults to "big".
See :func:`int_to_bytes` for more details.
:returns:
The decoded positive integer.
"""
if not value:
return 0
upper = len(value) #upper bound in bytes
if order == "native":
order = sys.byteorder
if order == "big":
itr = xrange(upper*8-8, -8, -8)
else:
assert order == "little"
itr = xrange(0, upper*8, 8)
return sum(
ord(value[idx]) << offset
for idx, offset in enumerate(itr)
)
def bytes_to_list(value, order="big"):
"""decode a string into a list of numeric values representing each of it's bytes.
This is similar to :func:`bytes_to_int`, the options and results
are effectively the same, except that this function
returns a list of numbers representing each byte in sequence,
with most significant byte listed first.
:arg value:
The string to decode.
:param order:
The byte ordering, defaults to "big".
See :func:`int_to_bytes` for more details.
:returns:
The decoded list of byte values.
"""
if order == "native":
order = sys.byteorder
if order == "big":
return [ ord(c) for c in value ]
else:
assert order == "little"
return [ ord(c) for c in reversed(value) ]
def _bytes_align(left, right, order):
"helper used by xor_bytes, and_bytes, etc. to align strings"
l = len(left)
r = len(right)
if l != r:
if order is None:
raise ValueError, "strings are not same size: %r %r" % (l, r)
if order == "native":
order = sys.byteorder
if order == "big":
#right-align strings by padding left with nulls
if l < r:
left = ("\x00" * (r-l)) + left
else:
right = ("\x00" * (l-r)) + right
else:
assert order == "little"
#left-align strings by padding right with nulls
if l < r:
left += ("\x00" * (r-l))
else:
right += ("\x00" * (l-r))
assert len(left) == len(right)
return left, right
def binop_bytes(left, right, op, order=None):
"""perform arbitrary bit-wise logical operation on two bit strings.
:arg left:
left bit string
:arg right:
right bit string
:arg op:
This should be a callable with the syntax ``op(left_value,right_value) -> result_value``.
It will be called for every byte in the two strings,
and will be passed each byte as an integer.
It should then return the resulting byte.
:param order:
This sets the byte ordering of the strings,
which only really effects how the function deals
with strings of different sizes.
=============== =====================================================
Value Action
--------------- -----------------------------------------------------
``None`` No byte ordering is specified (the default).
The strings must be exactly the same size,
or a :exc:`ValueError` will be raised.
``"big"`` Big-endian byte ordering.
If the two strings are of unequal lengths,
the smaller one will be right-aligned,
so that the least significant digits line up.
The resulting string will be as long as the
long of the two inputs.
``"little"`` Little-endian byte ordering.
If the two strings are of unequal lengths,
the smaller one will be left-aligned,
so that the least significant digits line up.
The resulting string will be as long as the
long of the two inputs.
``"native"`` The native byte ordering (``"little"`` or ``"big"``)
will be used.
=============== =====================================================
:returns:
The resulting bit string
"""
left, right = _bytes_align(left, right, order)
return "".join(
chr(op(ord(a), ord(b)))
for a, b in izip(left, right)
)
#bytes_xor
def xor_bytes(left, right, order=None):
"""XOR two bit strings together.
This is the equivalent of ``int_to_bytes(bytes_to_int(left) ^ bytes_to_int(right))``.
:arg left:
The first bit string to perform the operation on.
:arg right:
The second bit string to perform the operation on.
:param order:
The byte ordering to for aligning
strings of different sizes.
See :func:`binop_bytes` for details.
"""
left, right = _bytes_align(left, right, order)
return "".join(
chr(ord(a) ^ ord(b))
for a, b in izip(left, right)
)
#bytes_and
def and_bytes(left, right, order=None):
"""AND two bit strings together.
This is the equivalent of ``int_to_bytes(bytes_to_int(left) & bytes_to_int(right))``.
:arg left:
The first bit string to perform the operation on.
:arg right:
The second bit string to perform the operation on.
:param order:
The byte ordering to for aligning
strings of different sizes.
See :func:`binop_bytes` for details.
"""
left, right = _bytes_align(left, right, order)
return "".join(
chr(ord(a) & ord(b))
for a, b in izip(left, right)
)
#bytes_or
def or_bytes(left, right, order=None):
"""OR two bit strings together.
This is the equivalent of ``int_to_bytes(bytes_to_int(left) | bytes_to_int(right))``.
:arg left:
The first bit string to perform the operation on.
:arg right:
The second bit string to perform the operation on.
:param order:
The byte ordering to for aligning
strings of different sizes.
See :func:`binop_bytes` for details.
"""
left, right = _bytes_align(left, right, order)
return "".join(
chr(ord(a) | ord(b))
for a, b in izip(left, right)
)
#bytes_neg
def invert_bytes(value):
"""invert a bit string (1->0, 0->1)
This is the equivalent of ``int_to_bytes(~bytes_to_int(value))``.
"""
return "".join( chr(256+~ord(a)) for a in value)
#=========================================================
#counting systems
#=========================================================
#set of chars used by int_to_base()
_chars = "0123456789abcdefghijklmnopqrstuvwxyz"
def int_to_base(value, base=10, pad=0):
"""convert integer to specified base.
The builtin python function :func:`int`
has the option for converting integers from string
format using a variety of bases.
This is the inverse, which converts an integer
into a string of the specified base.
:arg value:
The integer to convert
:arg base:
The base to use. Must be between 2 and 36, inclusive.
:param pad:
Optionally add zeros to left of number until string is at least ``pad``
characters long.
It should always be true that ``int(to_base(n,b),b) == n``.
Usage Example::
>>> from bps.numeric import int_to_base
>>> int_to_base(123456789,32)
'3lnj8l'
>>> int('3lnj8l',32)
123456789
>>> int_to_base(123456789,16)
'75bcd15'
>>> 0x75bcd15
123456789
"""
if base < 2 or base > 36 or int(base) != base:
raise ValueError, "base must be between 2 and 36, inclusive: %r" % (base,)
if value == 0:
return "0"
if value < 0:
neg = True
value = -value
else:
neg = False
out = ""
while value > 0:
out = _chars[ value % base ] + out
value = int(value/base)
if pad and len(out) < pad:
out = ('0' * (pad-len(out))) + out
if neg:
out = "-" + out
return out
base_to_int = int #convience alias for reverse of int_to_base
def float_to_base(value, base, fsize=-1, ftrim=True):
"""convert float to specified base"""
if base < 2 or base > 36 or int(base) != base:
raise ValueError, "base must be between 2 and 36, inclusive: %r" % (base,)
#split into int & frac parts
fp, ip = modf(value)
assert int(ip) == ip
#format int part
text = int_to_base(int(ip), base)
if fsize == 0:
return text
text += "."
#determine default fsize
if fsize == -1: ##or fsize == -2:
#show digits to max system precision
bits = BPF
if ip:
bits -= 1+int(floor(log(abs(ip), 2))) #account for integer bits
if bits < 0:
fsize = 0
else:
fsize = int(ceil(log(1<<bits, base))) #num digits under base
#TODO: under fsize==-1 + ftrim,
# could implement "pick shortest repr" algorithm
# from py3.1
elif fsize < -1:
raise ValueError, "fsize must be >= -1: %r" % (fsize,)
#scale fp up to fsize, and round it
r, m = modf(abs(fp) * (base**fsize))
m = int(m)
if r >= .5:
m += 1
#render in reverse order
out = ''
for d in xrange(fsize):
out += _chars[m % base]
m //= base
#trim the zeroes, reverse, and return
if ftrim:
out = out.lstrip("0")
if not out:
out = "0"
return text + out[::-1]
#todo: base_to_float
#=========================================================
#roman numerals
#=========================================================
##def int_to_barred_roman(value):
## """convert integer to parsed list of roman numerals,
## suitable for rendering with overscores via post-processing.
## """
## #return tuple of roman numerals s.t.
## #last string in list should have no overscore,
## #2nd from last should have 1 overscore,
## #3rd from last should have 2 overscores, etc.
## out = []
## def helper(value, bars):
## if value >= 4000:
## #more than 4000, got to invoke special rules
## if value % 10000 < 4000:
## #put the thousands w/ current bar level, since <4000
## x = value//1000
## helper(x - (x%10), bars+1)
## value %= 10000
## else:
## #else put the thosands w/ next bar level, since >=4000
## helper(value//1000, bars+1)
## value %= 1000
## if value > 0:
## temp = int_to_roman(value)
## out.append((temp, bars))
## helper(value, 0)
## return out
_roman_level = "IVXLCDM"
_roman_decimal = "IXCM"
_roman_values = dict(I=1, V=5, X=10, L=50, C=100, D=500, M=1000)
_roman_standard = [
('M', 1000), ('CM', 900), ('D', 500), ('CD', 400),
('C', 100), ('XC', 90), ('L', 50), ('XL', 40),
('X', 10), ('IX', 9), ('V', 5), ('IV', 4),
('I', 1),
]
_roman_additive = [
('M', 1000), ('D', 500), ('C', 100), ('L', 50),
('X', 10), ('V', 5), ('I', 1),
]
def int_to_roman(value, dialect="standard"): ##, bar="~"):
"convert integer to roman numerals"
#disable till there's a need, and a parser...
## if mode == "barred":
## return "".join(
## "".join(
## (bar * count) + c
## for c in elem
## )
## for elem, count in int_to_barred_roman(value)
## )
if dialect == "additive":
pairs = _roman_additive
max_mult = 4
else:
assert dialect == "standard"
pairs = _roman_standard
max_mult = 3
max_value = 4*pairs[0][1]
if value < 1:
raise ValueError, "value too small: %r" % (value,)
if value >= max_value:
raise ValueError, "value too large: %r >= %r" % (value, max_value)
out = ''
for char, mag in pairs:
if value >= mag:
mult = value//mag
assert 1 <= mult <= max_mult
#^ thanks to 9/5/4 pairs, we shouldn't ever have mults larger
assert mult == 1 or char in _roman_decimal
#^ thanks to 1 pairs, 9/5/4 pairs shouldn only have 0/1 mult
out += char * mult
value -= mag * mult
assert value == 0
return out
def roman_to_int(value, strict=False):
"""convert roman numerals to integer.
This function accepts all properly formed roman numerals (eg ``"xiv"``),
but will also attempt grammatically incorrect strings (eg ``"iiiiv"``),
but will reject ones which aren't interpretable as a valid positive integer (eg ``"vvx"``).
Such invalid values will result in a ValueError.
:param strict:
If this is set to ``True``, the input must a a proper roman numeral.
That is to say, the subtraction only allows for
a single "I" before a "V" or "X", a single "X" before a "L" or "C",
and a single "C" before a "D" or "M", and all additive letters
must occur in decreasing value if they occur at all.
Under strict mode, any violations of this rule will cause a ValueError.
"""
orig = value
value = value.strip().upper()
if not value:
raise ValueError, "invalid literal for int_from_roman: %r" % (orig,)
if any(c not in _roman_level for c in value):
raise ValueError, "invalid literal for int_from_roman: %r" % (orig,)
if strict:
return _strict_parse_roman(orig, value)
else:
return _parse_roman(orig, value, len(value)-1, 999)[1]
def _strict_parse_roman(orig, value):
"parser used by int_from_roman"
out = 0
for char, mag in _roman_standard:
if char in _roman_decimal: #only IXCM are allowed to repeat
count = 0
while value.startswith(char):
if count == 4:
#max of 4 repetitions is allowed to permit additive style,
#5 is just plain too many
raise ValueError, "invalid synatax for int_from_roman: %r" % (orig,)
out += mag
value = value[len(char):]
count += 1
elif value.startswith(char): # VLD and the subtractive pairs can occur only once
out += mag
value = value[len(char):]
if value:
raise ValueError, "invalid syntax for int_from_roman: %r" % (orig,)
return out
def _parse_roman(orig, value, idx, stop_level):
"parser used by int_from_roman()"
out = 0
cur_level = -1
while idx > -1:
char = value[idx]
level = _roman_level.find(char)
if level >= stop_level:
#if we hit higher level, return value and cursor
return idx, out
if level < cur_level:
#if dropped down from last level, this is beginning of a substraction stanza,
#which will last for all chars from to the left of idx (including idx),
#until (but excluding) the first char w/ the same level as cur_level
idx, diff = _parse_roman(orig, value, idx, cur_level)
out -= diff
if out < 1:
raise ValueError, "invalid syntax for int_from_roman: %r" % (orig,)
else:
#else we're at old level or better
out += _roman_values[char]
cur_level = level
idx -= 1
return -1, out
#=========================================================
#misc
#=========================================================
##def iter_fibonacci():
## yield 1
## yield 1
## last = 1
## cur = 1
## while True:
## last, cur = cur, last+cur
## yield cur
##def seqsum(*seqs):
## """generate a list that contains the element-wise sum of all the sequences passed in.
## the result will be as long as the longest sequence passed in,
## and any shorter input sequences will be implicitly right-padded with zeros.
## """
#### if len(seqs) == 1 and isseq(seqs[0]):
#### seqs = seqs[0]
## if not seqs:
## return []
## if isnum(seqs[0]):
## assert len(seqs) % 2 == 0
## #assume it's a sequence of [weight1, seq1, weight2, seq2 ... ],
## size = max(len(seq) for seq in seqs[1::2])
## return [
## sum(
## seqs[col] * (
## seqs[col+1][idx] if idx < len(seqs[col+1]) else 0
## )
## for col in xrange(0, len(seqs), 2)
## )
## for idx in xrange(size)
## ]
## elif isseq(seqs[0]) and len(seqs[0]) == 2 and isseq(seqs[0][1]):
## #assume it's a sequence of [ (weight1, seq1), (weight2, seq2) ... ],
## size = max(len(row[1]) for row in seqs)
## return [
## sum(
## weight * (seq[idx] if idx < len(seq) else 0)
## for weight, seq in seqs
## )
## for idx in xrange(size)
## ]
## else:
## #assume it's a sequence of [seq1, seq2...]
## if len(seqs) == 1:
## return list(seqs[0])
## size = max(len(seq) for seq in seqs)
## return [
## sum((seq[idx] if idx < len(seq) else 0) for seq in seqs)
## for idx in xrange(size)
## ]
#=========================================================
#eof
#=========================================================
|