Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/sets/integer_range.py
4036 views
1
"""
2
Integer Range
3
4
AUTHORS:
5
6
- Nicolas Borie (2010-03): First release.
7
- Florent Hivert (2010-03): Added a class factory + cardinality method.
8
"""
9
#*****************************************************************************
10
# Copyright (C) 2010 Nicolas Borie <[email protected]>
11
#
12
# Distributed under the terms of the GNU General Public License (GPL)
13
# http://www.gnu.org/licenses/
14
#*****************************************************************************
15
16
from sage.structure.parent import Parent
17
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
18
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
19
from sage.structure.unique_representation import UniqueRepresentation
20
from sage.rings.integer import Integer
21
from sage.rings.integer_ring import IntegerRing
22
from sage.rings.infinity import Infinity, MinusInfinity, PlusInfinity
23
24
class IntegerRange(UniqueRepresentation, Parent):
25
r"""
26
The class of :class:`Integer <sage.rings.integer.Integer>` ranges
27
28
Returns an enumerated set containing an arithmetic progression of integers.
29
30
INPUT:
31
32
- ``begin`` -- an integer, Infinity or -Infinity
33
- ``end`` -- an integer, Infinity or -Infinity
34
- ``step`` -- a non zero integer (default to 1)
35
- ``middle_point`` -- an integer inside the set (default to ``None``)
36
37
OUTPUT:
38
39
A parent in the category :class:`FiniteEnumeratedSets()
40
<sage.categories.finite_enumerated_sets.FiniteEnumeratedSets>` or
41
:class:`InfiniteEnumeratedSets()
42
<sage.categories.infinite_enumerated_sets.InfiniteEnumeratedSets>`
43
depending on the arguments defining ``self``.
44
45
``IntegerRange(i, j)`` returns the set of `\{i, i+1, i+2, \dots , j-1\}`.
46
``start`` (!) defaults to 0. When ``step`` is given, it specifies the
47
increment. The default increment is `1`. IntegerRange allows ``begin`` and
48
``end`` to be infinite.
49
50
``IntegerRange`` is designed to have similar interface Python
51
range. However, whereas ``range`` accept and returns Python ``int``,
52
``IntegerRange`` deals with :class:`Integer <sage.rings.integer.Integer>`.
53
54
If ``middle_point`` is given, then the elements are generated starting
55
from it, in a alternating way: `\{m, m+1, m-2, m+2, m-2 \dots \}`.
56
57
EXAMPLES::
58
59
sage: list(IntegerRange(5))
60
[0, 1, 2, 3, 4]
61
sage: list(IntegerRange(2,5))
62
[2, 3, 4]
63
sage: I = IntegerRange(2,100,5); I
64
{2, 7, .., 97}
65
sage: list(I)
66
[2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97]
67
sage: I.category()
68
Category of facade finite enumerated sets
69
sage: I[1].parent()
70
Integer Ring
71
72
When ``begin`` and ``end`` are both finite, ``IntegerRange(begin, end,
73
step)`` is the set whose list of elements is equivalent to the python
74
construction ``range(begin, end, step)``::
75
76
sage: list(IntegerRange(4,105,3)) == range(4,105,3)
77
True
78
sage: list(IntegerRange(-54,13,12)) == range(-54,13,12)
79
True
80
81
Except for the type of the numbers::
82
83
sage: type(IntegerRange(-54,13,12)[0]), type(range(-54,13,12)[0])
84
(<type 'sage.rings.integer.Integer'>, <type 'int'>)
85
86
When ``begin`` is finite and ``end`` is +Infinity, ``self`` is the infinite
87
arithmetic progression starting from the ``begin`` by step ``step``::
88
89
sage: I = IntegerRange(54,Infinity,3); I
90
{54, 57, ..}
91
sage: I.category()
92
Category of facade infinite enumerated sets
93
sage: p = iter(I)
94
sage: (p.next(), p.next(), p.next(), p.next(), p.next(), p.next())
95
(54, 57, 60, 63, 66, 69)
96
97
sage: I = IntegerRange(54,-Infinity,-3); I
98
{54, 51, ..}
99
sage: I.category()
100
Category of facade infinite enumerated sets
101
sage: p = iter(I)
102
sage: (p.next(), p.next(), p.next(), p.next(), p.next(), p.next())
103
(54, 51, 48, 45, 42, 39)
104
105
When ``begin`` and ``end`` are both infinite, you will have to specify the
106
extra argument ``middle_point``. ``self`` is then defined by a point
107
and a progression/regression setting by ``step``. The enumeration
108
is done this way: (let us call `m` the ``middle_point``)
109
`\{m, m+step, m-step, m+2step, m-2step, m+3step, \dots \}`::
110
111
sage: I = IntegerRange(-Infinity,Infinity,37,-12); I
112
Integer progression containing -12 with increment 37 and bounded with -Infinity and +Infinity
113
sage: I.category()
114
Category of facade infinite enumerated sets
115
sage: -12 in I
116
True
117
sage: -15 in I
118
False
119
sage: p = iter(I)
120
sage: (p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next())
121
(-12, 25, -49, 62, -86, 99, -123, 136)
122
123
It is also possible to use the argument ``middle_point`` for other cases, finite
124
or infinite. The set will be the same as if you didn't give this extra argument
125
but the enumeration will begin with this ``middle_point``::
126
127
sage: I = IntegerRange(123,-12,-14); I
128
{123, 109, .., -3}
129
sage: list(I)
130
[123, 109, 95, 81, 67, 53, 39, 25, 11, -3]
131
sage: J = IntegerRange(123,-12,-14,25); J
132
Integer progression containing 25 with increment -14 and bounded with 123 and -12
133
sage: list(J)
134
[25, 11, 39, -3, 53, 67, 81, 95, 109, 123]
135
136
Remember that, like for range, if you define a non empty set, ``begin`` is
137
supposed to be included and ``end`` is supposed to be excluded. In the same
138
way, when you define a set with a ``middle_point``, the ``begin`` bound will
139
be supposed to be included and the ``end`` bound supposed to be excluded::
140
141
sage: I = IntegerRange(-100,100,10,0)
142
sage: J = range(-100,100,10)
143
sage: 100 in I
144
False
145
sage: 100 in J
146
False
147
sage: -100 in I
148
True
149
sage: -100 in J
150
True
151
sage: list(I)
152
[0, 10, -10, 20, -20, 30, -30, 40, -40, 50, -50, 60, -60, 70, -70, 80, -80, 90, -90, -100]
153
154
155
.. note::
156
157
The input is normalized so that::
158
159
sage: IntegerRange(1, 6, 2) is IntegerRange(1, 7, 2)
160
True
161
sage: IntegerRange(1, 8, 3) is IntegerRange(1, 10, 3)
162
True
163
164
TESTS::
165
166
sage: # Some category automatic tests
167
sage: TestSuite(IntegerRange(2,100,3)).run()
168
sage: TestSuite(IntegerRange(564,-12,-46)).run()
169
sage: TestSuite(IntegerRange(2,Infinity,3)).run()
170
sage: TestSuite(IntegerRange(732,-Infinity,-13)).run()
171
sage: TestSuite(IntegerRange(-Infinity,Infinity,3,2)).run()
172
sage: TestSuite(IntegerRange(56,Infinity,12,80)).run()
173
sage: TestSuite(IntegerRange(732,-12,-2743,732)).run()
174
sage: # 20 random tests: range and IntegerRange give the same set for finite cases
175
sage: for i in range(20):
176
... begin = Integer(randint(-300,300))
177
... end = Integer(randint(-300,300))
178
... step = Integer(randint(-20,20))
179
... if step == 0:
180
... step = Integer(1)
181
... assert list(IntegerRange(begin, end, step)) == range(begin, end, step)
182
sage: # 20 random tests: range and IntegerRange with middle point for finite cases
183
sage: for i in range(20):
184
... begin = Integer(randint(-300,300))
185
... end = Integer(randint(-300,300))
186
... step = Integer(randint(-15,15))
187
... if step == 0:
188
... step = Integer(-3)
189
... I = IntegerRange(begin, end, step)
190
... if I.cardinality() == 0:
191
... assert len(range(begin, end, step)) == 0
192
... else:
193
... TestSuite(I).run()
194
... L1 = list(IntegerRange(begin, end, step, I.an_element()))
195
... L2 = range(begin, end, step)
196
... L1.sort()
197
... L2.sort()
198
... assert L1 == L2
199
200
Thanks to #8543 empty integer range are allowed::
201
202
sage: TestSuite(IntegerRange(0, 5, -1)).run()
203
"""
204
205
@staticmethod
206
def __classcall_private__(cls, begin, end=None, step=Integer(1), middle_point=None):
207
"""
208
TESTS::
209
210
sage: IntegerRange(2,5,0)
211
Traceback (most recent call last):
212
...
213
ValueError: IntegerRange() step argument must not be zero
214
sage: IntegerRange(2) is IntegerRange(0, 2)
215
True
216
"""
217
if end is None:
218
end = begin
219
begin = Integer(0)
220
# check of the arguments
221
assert isinstance(begin, (Integer, MinusInfinity, PlusInfinity))
222
assert isinstance(end, (Integer, MinusInfinity, PlusInfinity))
223
assert isinstance(step, Integer)
224
if step.is_zero():
225
raise ValueError("IntegerRange() step argument must not be zero")
226
227
# If begin and end are infinite, middle_point and step will defined the set.
228
if begin == -Infinity and end == Infinity:
229
if middle_point == None:
230
raise ValueError("Can't iterate over this set, please provide middle_point")
231
232
# If we have a middle point, we go on the special enumeration way...
233
if middle_point != None:
234
return IntegerRangeFromMiddle(begin, end, step, middle_point)
235
236
if (begin == -Infinity) or (begin == Infinity):
237
raise ValueError("Can't iterate over this set: It is impossible to begin an enumeration with plus/minus Infinity")
238
239
# Check for empty sets
240
if step > 0 and begin >= end or step < 0 and begin <= end:
241
return IntegerRangeEmpty()
242
243
if end != Infinity and end != -Infinity:
244
# Normalize the input
245
sgn = 1 if step > 0 else -1
246
end = begin+((end-begin-sgn)//(step)+1)*step
247
return IntegerRangeFinite(begin, end, step)
248
else:
249
return IntegerRangeInfinite(begin, step)
250
251
def _element_constructor_(self, el):
252
"""
253
TESTS::
254
255
sage: S = IntegerRange(1, 10, 2)
256
sage: S(1)
257
1
258
sage: S(0)
259
Traceback (most recent call last):
260
...
261
ValueError: 0 not in {1, 3, .., 9}
262
"""
263
if el in self:
264
return el
265
else:
266
raise ValueError, "%s not in %s"%(el, self)
267
268
element_class = Integer
269
270
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
271
class IntegerRangeEmpty(IntegerRange, FiniteEnumeratedSet):
272
r"""
273
A singleton class for empty integer ranges
274
275
See :class:`IntegerRange` for more details.
276
"""
277
278
# Needed because FiniteEnumeratedSet.__classcall__ takes an argument.
279
@staticmethod
280
def __classcall__(cls, *args):
281
"""
282
TESTS::
283
284
sage: from sage.sets.integer_range import IntegerRangeEmpty
285
sage: I = IntegerRangeEmpty(); I
286
{}
287
sage: I.category()
288
Category of facade finite enumerated sets
289
sage: TestSuite(I).run()
290
sage: I(0)
291
Traceback (most recent call last):
292
...
293
ValueError: 0 not in {}
294
"""
295
return FiniteEnumeratedSet.__classcall__(cls, ())
296
297
class IntegerRangeFinite(IntegerRange):
298
r"""
299
The class of finite enumerated sets of integers defined by finite
300
arithmetic progressions
301
302
See :class:`IntegerRange` for more details.
303
"""
304
def __init__(self, begin, end, step=Integer(1)):
305
r"""
306
TESTS::
307
308
sage: I = IntegerRange(123,12,-4)
309
sage: I.category()
310
Category of facade finite enumerated sets
311
sage: TestSuite(I).run()
312
"""
313
self._begin = begin
314
self._end = end
315
self._step = step
316
Parent.__init__(self, facade = IntegerRing(), category = FiniteEnumeratedSets())
317
318
def __contains__(self, elt):
319
r"""
320
Returns True if ``elt`` is in ``self``.
321
322
EXAMPLES::
323
324
sage: I = IntegerRange(123,12,-4)
325
sage: 123 in I
326
True
327
sage: 127 in I
328
False
329
sage: 12 in I
330
False
331
sage: 13 in I
332
False
333
sage: 14 in I
334
False
335
sage: 15 in I
336
True
337
sage: 11 in I
338
False
339
"""
340
if isinstance(elt, Integer):
341
if (self._step.__abs__()).divides(Integer(elt)-self._begin):
342
return (self._begin <= elt < self._end and self._step > 0) or \
343
(self._begin >= elt > self._end and self._step < 0)
344
else:
345
return False
346
else:
347
return False
348
349
def cardinality(self):
350
"""
351
Return the cardinality of ``self``
352
353
EXAMPLES::
354
355
sage: IntegerRange(123,12,-4).cardinality()
356
28
357
sage: IntegerRange(-57,12,8).cardinality()
358
9
359
sage: IntegerRange(123,12,4).cardinality()
360
0
361
"""
362
return (abs((self._end+self._step-self._begin))-1) // abs(self._step)
363
364
def _repr_(self):
365
"""
366
EXAMPLES::
367
368
sage: IntegerRange(1,2)
369
{1}
370
sage: IntegerRange(1,3)
371
{1, 2}
372
sage: IntegerRange(1,5,3)
373
{1, 4}
374
sage: IntegerRange(1,12)
375
{1, .., 11}
376
sage: IntegerRange(123,12,-4)
377
{123, 119, .., 15}
378
sage: IntegerRange(-57,1,3)
379
{-57, -54, .., 0}
380
381
sage: IntegerRange(-57,Infinity,8)
382
{-57, -49, ..}
383
sage: IntegerRange(-112,-Infinity,-13)
384
{-112, -125, ..}
385
"""
386
card = self.cardinality()
387
if card == 1:
388
return "{%s}"%self._begin
389
elif card == 2:
390
return "{%s, %s}"%(self._begin, self._begin+self._step)
391
elif self._step == 1:
392
return "{%s, .., %s}"%(self._begin, self._end-self._step)
393
else:
394
return "{%s, %s, .., %s}"%(self._begin, self._begin+self._step,
395
self._end-self._step)
396
397
def __iter__(self):
398
r"""
399
Returns an iterator over the elements of ``self``
400
401
EXAMPLES::
402
403
sage: I = IntegerRange(123,12,-4)
404
sage: p = iter(I)
405
sage: [p.next() for i in range(8)]
406
[123, 119, 115, 111, 107, 103, 99, 95]
407
sage: I = IntegerRange(-57,12,8)
408
sage: p = iter(I)
409
sage: [p.next() for i in range(8)]
410
[-57, -49, -41, -33, -25, -17, -9, -1]
411
"""
412
n = self._begin
413
if self._step > 0:
414
while n < self._end:
415
yield n
416
n += self._step
417
else:
418
while n > self._end:
419
yield n
420
n += self._step
421
422
def _an_element_(self):
423
r"""
424
Returns an element of ``self``.
425
426
EXAMPLES::
427
428
sage: I = IntegerRange(123,12,-4)
429
sage: I.an_element()
430
115
431
sage: I = IntegerRange(-57,12,8)
432
sage: I.an_element()
433
-41
434
"""
435
p = (self._begin + 2*self._step)
436
if p in self:
437
return p
438
else:
439
return self._begin
440
441
class IntegerRangeInfinite(IntegerRange):
442
r""" The class of infinite enumerated sets of integers defined by infinite
443
arithmetic progressions.
444
445
See :class:`IntegerRange` for more details.
446
"""
447
def __init__(self, begin, step=Integer(1)):
448
r"""
449
TESTS::
450
451
sage: I = IntegerRange(-57,Infinity,8)
452
sage: I.category()
453
Category of facade infinite enumerated sets
454
sage: TestSuite(I).run()
455
"""
456
assert isinstance(begin, Integer)
457
self._begin = begin
458
self._step = step
459
Parent.__init__(self, facade = IntegerRing(), category = InfiniteEnumeratedSets())
460
461
def _repr_(self):
462
r"""
463
TESTS::
464
465
sage: IntegerRange(123,12,-4)
466
{123, 119, .., 15}
467
sage: IntegerRange(-57,1,3)
468
{-57, -54, .., 0}
469
470
sage: IntegerRange(-57,Infinity,8)
471
{-57, -49, ..}
472
sage: IntegerRange(-112,-Infinity,-13)
473
{-112, -125, ..}
474
"""
475
return "{%s, %s, ..}"%(self._begin, self._begin+self._step)
476
477
def __contains__(self, elt):
478
r"""
479
Returns True if ``elt`` is in ``self``.
480
481
EXAMPLES::
482
483
sage: I = IntegerRange(-57,Infinity,8)
484
sage: -57 in I
485
True
486
sage: -65 in I
487
False
488
sage: -49 in I
489
True
490
sage: 743 in I
491
True
492
"""
493
if isinstance(elt, Integer):
494
if (self._step.__abs__()).divides(Integer(elt)-self._begin):
495
return (self._step > 0 and elt >= self._begin) or \
496
(self._step < 0 and elt <= self._begin)
497
else:
498
return False
499
else:
500
return False
501
502
def __iter__(self):
503
r"""
504
Returns an iterator over the elements of ``self``.
505
506
EXAMPLES::
507
508
sage: I = IntegerRange(-57,Infinity,8)
509
sage: p = iter(I)
510
sage: [p.next() for i in range(8)]
511
[-57, -49, -41, -33, -25, -17, -9, -1]
512
513
sage: I = IntegerRange(-112,-Infinity,-13)
514
sage: p = iter(I)
515
sage: [p.next() for i in range(8)]
516
[-112, -125, -138, -151, -164, -177, -190, -203]
517
"""
518
n = self._begin
519
while True:
520
yield n
521
n += self._step
522
523
def _an_element_(self):
524
r"""
525
Returns an element of ``self``.
526
527
EXAMPLES::
528
529
sage: I = IntegerRange(-57,Infinity,8)
530
sage: I.an_element()
531
191
532
533
sage: I = IntegerRange(-112,-Infinity,-13)
534
sage: I.an_element()
535
-515
536
"""
537
return self._begin+31*self._step
538
539
540
class IntegerRangeFromMiddle(IntegerRange):
541
r"""
542
The class of finite or infinite enumerated sets defined with
543
an inside point, a progression and two limits.
544
545
See :class:`IntegerRange` for more details.
546
"""
547
def __init__(self, begin, end, step=Integer(1), middle_point=Integer(1)):
548
r"""
549
TESTS::
550
551
sage: from sage.sets.integer_range import IntegerRangeFromMiddle
552
sage: I = IntegerRangeFromMiddle(-100,100,10,0)
553
sage: I.category()
554
Category of facade finite enumerated sets
555
sage: TestSuite(I).run()
556
sage: I = IntegerRangeFromMiddle(Infinity,-Infinity,-37,0)
557
sage: I.category()
558
Category of facade infinite enumerated sets
559
sage: TestSuite(I).run()
560
561
sage: IntegerRange(0, 5, 1, -3)
562
Traceback (most recent call last):
563
...
564
AssertionError: middle_point is not in the interval
565
"""
566
self._begin = begin
567
self._end = end
568
self._step = step
569
self._middle_point = middle_point
570
assert middle_point in self, "middle_point is not in the interval"
571
572
if (begin != Infinity and begin != -Infinity) and \
573
(end != Infinity and end != -Infinity):
574
Parent.__init__(self, facade = IntegerRing(), category = FiniteEnumeratedSets())
575
else:
576
Parent.__init__(self, facade = IntegerRing(), category = InfiniteEnumeratedSets())
577
578
def __repr__(self):
579
r"""
580
TESTS::
581
582
sage: from sage.sets.integer_range import IntegerRangeFromMiddle
583
sage: IntegerRangeFromMiddle(Infinity,-Infinity,-37,0)
584
Integer progression containing 0 with increment -37 and bounded with +Infinity and -Infinity
585
sage: IntegerRangeFromMiddle(-100,100,10,0)
586
Integer progression containing 0 with increment 10 and bounded with -100 and 100
587
"""
588
return "Integer progression containing %s with increment %s and bounded with %s and %s"%(self._middle_point,self._step,self._begin,self._end)
589
590
def __contains__(self, elt):
591
r"""
592
Returns True if ``elt`` is in ``self``.
593
594
EXAMPLES::
595
596
sage: from sage.sets.integer_range import IntegerRangeFromMiddle
597
sage: I = IntegerRangeFromMiddle(-100,100,10,0)
598
sage: -110 in I
599
False
600
sage: -100 in I
601
True
602
sage: 30 in I
603
True
604
sage: 90 in I
605
True
606
sage: 100 in I
607
False
608
"""
609
if isinstance(elt, Integer):
610
if (self._step.__abs__()).divides(Integer(elt)-self._middle_point):
611
return (self._begin <= elt and elt < self._end) or \
612
(self._begin >= elt and elt > self._end)
613
else:
614
return False
615
else:
616
return False
617
618
def next(self, elt):
619
r"""
620
Return the next element of ``elt`` in ``self``.
621
622
EXAMPLES::
623
624
sage: from sage.sets.integer_range import IntegerRangeFromMiddle
625
sage: I = IntegerRangeFromMiddle(-100,100,10,0)
626
sage: (I.next(0), I.next(10), I.next(-10), I.next(20), I.next(-100))
627
(10, -10, 20, -20, None)
628
sage: I = IntegerRangeFromMiddle(-Infinity,Infinity,10,0)
629
sage: (I.next(0), I.next(10), I.next(-10), I.next(20), I.next(-100))
630
(10, -10, 20, -20, 110)
631
"""
632
assert (elt in self)
633
n = self._middle_point
634
if (elt <= n and self._step > 0) or (elt >= n and self._step < 0):
635
right = 2*n-elt+self._step
636
if right in self:
637
return right
638
else:
639
left = elt-self._step
640
if left in self:
641
return left
642
else:
643
left = 2*n-elt
644
if left in self:
645
return left
646
else:
647
right = elt+self._step
648
if right in self:
649
return right
650
651
def __iter__(self):
652
r"""
653
Returns an iterator over the elements of ``self``.
654
655
EXAMPLES::
656
657
sage: from sage.sets.integer_range import IntegerRangeFromMiddle
658
sage: I = IntegerRangeFromMiddle(Infinity,-Infinity,-37,0)
659
sage: p = iter(I)
660
sage: (p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next())
661
(0, -37, 37, -74, 74, -111, 111, -148)
662
sage: I = IntegerRangeFromMiddle(-12,214,10,0)
663
sage: p = iter(I)
664
sage: (p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next(), p.next())
665
(0, 10, -10, 20, 30, 40, 50, 60)
666
"""
667
n = self._middle_point
668
while n != None:
669
yield n
670
n = self.next(n)
671
672
def _an_element_(self):
673
r"""
674
Returns an element of ``self``.
675
676
EXAMPLES::
677
678
sage: from sage.sets.integer_range import IntegerRangeFromMiddle
679
sage: I = IntegerRangeFromMiddle(Infinity,-Infinity,-37,0)
680
sage: I.an_element()
681
0
682
sage: I = IntegerRangeFromMiddle(-12,214,10,0)
683
sage: I.an_element()
684
0
685
"""
686
return self._middle_point
687
688