istr-python 1.1.13__tar.gz → 1.1.16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: istr-python
3
- Version: 1.1.13
3
+ Version: 1.1.16
4
4
  Summary: istr - strings you can count on
5
5
  Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/salabim/istr
@@ -607,6 +607,17 @@ In contrast to `math.sumprod()`, `istr.sumprod()` supports a `strict` parameter
607
607
  Thus, `istr.sumprod("12", (3,4,5), strict=False)` is `istr(11)`, whereas `istr.sumprod("12", (3,4,5))`
608
608
  raises a ValueError.
609
609
 
610
+ #### get all squares, cubes, power ofs or primes in a given range
611
+
612
+ The class methods `istr.squares`, `istr.cubes` and `istr.primes` can be used to get a list of all squares, cubes or primes up to a given upperbound (non inclusive) or between a given lowerbound and upperbound (non inclusive), like:
613
+
614
+ `istr.squares (100)` returns a list of all squares <100
615
+ `istr.squares(50, 100)` return a list of all squares >=50 and <100
616
+
617
+ Unless `cache=False` is specified, the result of the query is cached.
618
+
619
+ The same functionality is available for cubes, power ofs and primes
620
+
610
621
  #### generate istr with digits
611
622
 
612
623
  The class method `digits` can be used to return an istr of digits according to a given specification.
@@ -594,6 +594,17 @@ In contrast to `math.sumprod()`, `istr.sumprod()` supports a `strict` parameter
594
594
  Thus, `istr.sumprod("12", (3,4,5), strict=False)` is `istr(11)`, whereas `istr.sumprod("12", (3,4,5))`
595
595
  raises a ValueError.
596
596
 
597
+ #### get all squares, cubes, power ofs or primes in a given range
598
+
599
+ The class methods `istr.squares`, `istr.cubes` and `istr.primes` can be used to get a list of all squares, cubes or primes up to a given upperbound (non inclusive) or between a given lowerbound and upperbound (non inclusive), like:
600
+
601
+ `istr.squares (100)` returns a list of all squares <100
602
+ `istr.squares(50, 100)` return a list of all squares >=50 and <100
603
+
604
+ Unless `cache=False` is specified, the result of the query is cached.
605
+
606
+ The same functionality is available for cubes, power ofs and primes
607
+
597
608
  #### generate istr with digits
598
609
 
599
610
  The class method `digits` can be used to return an istr of digits according to a given specification.
@@ -5,7 +5,7 @@
5
5
  # |_||___/ \__||_|
6
6
  # strings you can count on
7
7
 
8
- __version__ = "1.1.13"
8
+ __version__ = "1.1.16"
9
9
  import functools
10
10
  import itertools
11
11
  import types
@@ -397,13 +397,19 @@ class istr(str):
397
397
  return not istr.is_divisible_by(self, 2)
398
398
 
399
399
  def is_divisible_by(self, divisor):
400
- return istr.interpret_as_int(self) % int(divisor) == 0
400
+ return divisor!=0 and istr.interpret_as_int(self) % int(divisor) == 0
401
401
 
402
402
  def is_square(self):
403
- return istr.is_power_of(self, 2)
403
+ n = istr.interpret_as_int(self)
404
+ if n < 1000000:
405
+ return n in _squares_up_to_1_000_000()
406
+ return n == round(n ** (1 / 2)) ** 2
404
407
 
405
408
  def is_cube(self):
406
- return istr.is_power_of(self, 3)
409
+ n = istr.interpret_as_int(self)
410
+ if n < 1000000:
411
+ return n in _cubes_up_to_1_000_000()
412
+ return n == round(n ** (1 / 3)) ** 3
407
413
 
408
414
  def is_power_of(self, exponent):
409
415
  n = istr.interpret_as_int(self)
@@ -411,7 +417,7 @@ class istr(str):
411
417
  raise ValueError(f"exponent must be >=1; not {exponent}")
412
418
  if not isinstance(exponent, int):
413
419
  raise TypeError(f"exponent must be int; not {type(exponent)}")
414
- return n >= 0 and self == round(n ** (1 / exponent)) ** exponent
420
+ return n >= 0 and n == round(n ** (1 / exponent)) ** exponent
415
421
 
416
422
  def is_prime(self):
417
423
  n = istr.interpret_as_int(self)
@@ -421,12 +427,50 @@ class istr(str):
421
427
  return True
422
428
  if not n & 1:
423
429
  return False
424
-
430
+ if n < 1000000:
431
+ return n in _primes_up_to_1_000_000()
425
432
  for x in range(3, int(n**0.5) + 1, 2):
426
433
  if n % x == 0:
427
434
  return False
428
435
  return True
429
436
 
437
+ @classmethod
438
+ def primes(cls, lb_or_ub, ub=None, cache=True):
439
+ """
440
+ returns all primes up to a given upperbound or between a given lowerbound and upperbound
441
+ """
442
+ if ("primes", lb_or_ub, ub) in _cache:
443
+ return _cache["primes", lb_or_ub, ub]
444
+ result = sorted(map(cls, _primes(lb_or_ub, ub)))
445
+ if cache:
446
+ _cache["primes", lb_or_ub, ub]=result
447
+ return result
448
+
449
+ @classmethod
450
+ def squares(cls, lb_or_ub, ub=None, cache=True):
451
+ """
452
+ returns all squares up to a given upperbound or between a given lowerbound and upperbound
453
+ """
454
+ return istr.power_ofs(2,lb_or_ub, ub, cache=cache)
455
+
456
+ @classmethod
457
+ def cubes(cls, lb_or_ub, ub=None, cache=True):
458
+ return istr.power_ofs(3,lb_or_ub, ub, cache=cache)
459
+
460
+ @classmethod
461
+ def power_ofs(cls, n,lb_or_ub, ub=None, cache=True):
462
+ """
463
+ returns all power of n up to a given upperbound or between a given lowerbound and upperbound
464
+ """
465
+ if ("power_ofs", n, lb_or_ub, ub) in _cache:
466
+ return _cache["power_ofs", n, lb_or_ub, ub]
467
+ result = sorted(map(cls, _power_ofs(n,lb_or_ub, ub)))
468
+ if cache:
469
+ _cache["power_ofs", n, lb_or_ub, ub]=result
470
+ return result
471
+
472
+
473
+
430
474
  def decompose(self, letters, namespace=None):
431
475
  """
432
476
  decompose one-letter variables into global variables
@@ -442,7 +486,7 @@ class istr(str):
442
486
  if letter in lookup and lookup[letter] != ch:
443
487
  raise ValueError(f"multiple values found for variable {letter}")
444
488
  if not letter.isidentifier():
445
- raise ValueError(f"{letter} cannot be used as a variable")
489
+ raise ValueError(f"{repr(letter)} cannot be used as a variable")
446
490
  lookup[str(letter)] = ch
447
491
  if len(letters) != len(self):
448
492
  raise ValueError(f"incorrect number of variables {len(letters)}; should be {len(self)}")
@@ -457,7 +501,7 @@ class istr(str):
457
501
  namespace = inspect.currentframe().f_back.f_globals
458
502
  for letter in letters:
459
503
  if letter not in namespace:
460
- raise ValueError(f"variable {letter} not defined")
504
+ raise ValueError(f"variable {repr(letter)} not defined")
461
505
  s = "".join(str(namespace[letter]) for letter in letters)
462
506
  return istr(s)
463
507
 
@@ -504,7 +548,7 @@ class istr(str):
504
548
 
505
549
  def is_triangular(self):
506
550
  n = istr.interpret_as_int(self)
507
- if n<=0:
551
+ if n <= 0:
508
552
  return False
509
553
  return istr.is_square(n * 8 + 1)
510
554
 
@@ -544,13 +588,13 @@ class istr(str):
544
588
  return self._as_int is not self._nan
545
589
 
546
590
  def join(self, iterable=None):
547
- if isinstance(self,istr):
591
+ if isinstance(self, istr):
548
592
  return istr(str(self).join(iterable))
549
593
  else:
550
594
  if iterable is None:
551
- return istr('').join(self)
595
+ return istr("").join(self)
552
596
  else:
553
- if not isinstance(self,str):
597
+ if not isinstance(self, str):
554
598
  raise TypeError(f"{self!r} should be str, not {type(self)}")
555
599
  return istr(self).join(iterable)
556
600
 
@@ -596,7 +640,8 @@ class istr(str):
596
640
 
597
641
  cls._int_format = int_format
598
642
 
599
- def __enter__(self): ...
643
+ def __enter__(self):
644
+ ...
600
645
 
601
646
  def __exit__(self, exc_type, exc_value, exc_tb):
602
647
  self.saved_cls._int_format = self.saved_int_format
@@ -617,7 +662,8 @@ class istr(str):
617
662
  self.saved_cls = cls
618
663
  cls._repr_mode = mode
619
664
 
620
- def __enter__(self): ...
665
+ def __enter__(self):
666
+ ...
621
667
 
622
668
  def __exit__(self, exc_type, exc_value, exc_tb):
623
669
  self.saved_cls._repr_mode = self.saved_repr_mode
@@ -636,7 +682,8 @@ class istr(str):
636
682
  self.saved_cls = cls
637
683
  cls._base = base
638
684
 
639
- def __enter__(self): ...
685
+ def __enter__(self):
686
+ ...
640
687
 
641
688
  def __exit__(self, exc_type, exc_value, exc_tb):
642
689
  self.saved_cls._base = self.saved_base
@@ -749,10 +796,52 @@ def _map(func, *iterables, strict=False):
749
796
 
750
797
  yield func(*values)
751
798
 
799
+ _cache={}
752
800
 
753
- istr.type = type(istr(0))
801
+ def _primes(lb_or_ub, ub=None):
802
+ lb, ub = (0, lb_or_ub) if ub is None else (lb_or_ub, ub)
803
+ sieve = bytearray(b"\x01") * (ub + 1)
804
+ sieve[0:2] = b"\x00\x00"
805
+
806
+ for i in range(2, int(ub**0.5)+1 ):
807
+ if sieve[i]:
808
+ sieve[i * i : ub + 1 : i] = b"\x00" * (((ub - i * i) // i) + 1)
809
+
810
+ return {i for i, is_prime in enumerate(sieve) if is_prime and lb<=i<ub}
811
+
812
+
813
+ @functools.lru_cache(maxsize=1)
814
+ def _primes_up_to_1_000_000():
815
+ return _primes(1000000)
754
816
 
755
817
 
818
+ @functools.lru_cache(maxsize=1)
819
+ def _squares_up_to_1_000_000():
820
+ return _power_ofs(2,1000000)
821
+
822
+ @functools.lru_cache(maxsize=1)
823
+ def _cubes_up_to_1_000_000():
824
+ return _power_ofs(3,1000000)
825
+
826
+
827
+ def _power_ofs(n, lb_or_ub, ub=None):
828
+ lb, ub = (0, lb_or_ub) if ub is None else (lb_or_ub, ub)
829
+ result = set()
830
+ if n==0:
831
+ if lb<=1<ub:
832
+ result.add(1)
833
+ elif n==1:
834
+ result={*range(lb,ub)}
835
+ else:
836
+ i = int(lb ** (1 / n))
837
+ while (in_ := i**n) < ub:
838
+ if in_ >= lb:
839
+ result.add(in_)
840
+ i += 1
841
+ return result
842
+
843
+ istr.type = type(istr(0))
844
+
756
845
  class istrModule(types.ModuleType):
757
846
  def __call__(self, *args, **kwargs):
758
847
  return istr(*args, **kwargs)
@@ -766,3 +855,4 @@ class istrModule(types.ModuleType):
766
855
 
767
856
  if __name__ != "__main__":
768
857
  sys.modules["istr"].__class__ = istrModule
858
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: istr-python
3
- Version: 1.1.13
3
+ Version: 1.1.16
4
4
  Summary: istr - strings you can count on
5
5
  Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/salabim/istr
@@ -607,6 +607,17 @@ In contrast to `math.sumprod()`, `istr.sumprod()` supports a `strict` parameter
607
607
  Thus, `istr.sumprod("12", (3,4,5), strict=False)` is `istr(11)`, whereas `istr.sumprod("12", (3,4,5))`
608
608
  raises a ValueError.
609
609
 
610
+ #### get all squares, cubes, power ofs or primes in a given range
611
+
612
+ The class methods `istr.squares`, `istr.cubes` and `istr.primes` can be used to get a list of all squares, cubes or primes up to a given upperbound (non inclusive) or between a given lowerbound and upperbound (non inclusive), like:
613
+
614
+ `istr.squares (100)` returns a list of all squares <100
615
+ `istr.squares(50, 100)` return a list of all squares >=50 and <100
616
+
617
+ Unless `cache=False` is specified, the result of the query is cached.
618
+
619
+ The same functionality is available for cubes, power ofs and primes
620
+
610
621
  #### generate istr with digits
611
622
 
612
623
  The class method `digits` can be used to return an istr of digits according to a given specification.
@@ -10,7 +10,7 @@ authors = [
10
10
  { name = "Ruud van der Ham", email = "rt.van.der.ham@gmail.com" },
11
11
  ]
12
12
  description = "istr - strings you can count on"
13
- version = "1.1.13"
13
+ version = "1.1.16"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.8"
16
16
  dependencies = []
@@ -6,6 +6,7 @@ import re
6
6
 
7
7
  if __name__ == "__main__": # to make the tests run without the pytest cli
8
8
  import os, sys # three lines to use the local package and chdir
9
+
9
10
  os.chdir(os.path.dirname(__file__))
10
11
  sys.path.insert(0, os.path.dirname(__file__) + "/../")
11
12
 
@@ -186,8 +187,8 @@ def test_range():
186
187
 
187
188
  with pytest.raises(IndexError):
188
189
  one_to_twelve[12]
189
-
190
- assert str(list(istr.range(5, base=2, repr_mode='str'))) == "['0', '1', '10', '11', '100']"
190
+
191
+ assert str(list(istr.range(5, base=2, repr_mode="str"))) == "['0', '1', '10', '11', '100']"
191
192
 
192
193
 
193
194
  def test_misc():
@@ -357,6 +358,9 @@ def test_is_square():
357
358
  assert not istr.is_square(2)
358
359
  assert istr.is_square(4)
359
360
  assert istr.is_square(16)
361
+ assert istr.is_square(1_000_000)
362
+ assert istr.is_square(12345**2)
363
+ assert not istr.is_square(12345**2 + 1)
360
364
 
361
365
 
362
366
  def test_is_cube():
@@ -374,6 +378,8 @@ def test_is_cube():
374
378
  assert not istr.is_cube(2)
375
379
  assert istr.is_cube(8)
376
380
  assert istr.is_cube(27)
381
+ assert istr.is_cube(12345**3)
382
+ assert not istr.is_cube(12345**3 + 1)
377
383
 
378
384
 
379
385
  def test_is_power_of():
@@ -414,6 +420,32 @@ def test_is_prime():
414
420
  assert not istr.is_prime(4)
415
421
  assert istr.is_prime(97)
416
422
  assert not istr.is_prime(99)
423
+ assert not istr(100000003).is_prime()
424
+ assert istr(100000007).is_prime()
425
+
426
+
427
+ def test_primes():
428
+ assert istr.primes(17) == [istr("2"), istr("3"), istr("5"), istr("7"), istr("11"), istr("13")]
429
+ assert istr.primes(40, 50) == [istr("41"), istr("43"), istr("47")]
430
+ assert id(istr.primes(100)) == id(istr.primes(100)) # test caching
431
+
432
+
433
+ def test_squares():
434
+ assert istr.squares(50) == [istr("0"), istr("1"), istr("4"), istr("9"), istr("16"), istr("25"), istr("36"), istr("49")]
435
+ assert istr.squares(40, 50) == [istr("49")]
436
+ assert id(istr.squares(100)) == id(istr.squares(100)) # test caching
437
+
438
+
439
+ def test_cubes():
440
+ assert istr.cubes(50) == [istr("0"), istr("1"), istr("8"), istr("27")]
441
+ assert istr.cubes(27, 50) == [istr("27")]
442
+ assert id(istr.cubes(100)) == id(istr.cubes(100)) # test caching
443
+
444
+ def test_power_ofs():
445
+ assert istr.power_ofs(3,1,50) == [istr("1"), istr("8"), istr("27")]
446
+ assert istr.power_ofs(5,1, 500) == [istr('1'), istr('32'), istr('243')]
447
+ assert id(istr.power_ofs(3,2000)) != id(istr.cubes(3,2000)) # test caching
448
+ assert id(istr.power_ofs(3,1000,cache=False)) != id(istr.cubes(3,1000,cache=False)) # test caching
417
449
 
418
450
 
419
451
  def test_join():
@@ -437,15 +469,15 @@ def test_join():
437
469
  assert type(s) is istr.type
438
470
 
439
471
  s = istr.join("123")
440
- assert s =="123"
472
+ assert s == "123"
441
473
  assert type(s) is istr.type
442
474
 
443
- s = istr.join(("1","2","3"))
444
- assert s =="123"
475
+ s = istr.join(("1", "2", "3"))
476
+ assert s == "123"
445
477
  assert type(s) is istr.type
446
478
 
447
- s = istr.join(", ", ("1","2","3"))
448
- assert s =="1, 2, 3"
479
+ s = istr.join(", ", ("1", "2", "3"))
480
+ assert s == "1, 2, 3"
449
481
  assert type(s) is istr.type
450
482
 
451
483
  with pytest.raises(TypeError):
@@ -454,6 +486,7 @@ def test_join():
454
486
  with pytest.raises(TypeError):
455
487
  istr.join("a", 12)
456
488
 
489
+
457
490
  def test_or():
458
491
  assert (eleven | twelve).equals(istr("1112"))
459
492
  assert ("11" | twelve).equals(istr("1112"))
@@ -694,20 +727,22 @@ def test_base():
694
727
  assert istr(a, base=36) == "73"
695
728
  assert istr(x) == "?"
696
729
  assert istr(x, base=36) == "?"
697
-
730
+
731
+
698
732
  def test_this_queries():
699
733
  a = istr(12, base=36, int_format="04", repr_mode="str")
700
- assert repr(a)=="'C'"
701
- assert a.this_base()==36
702
- assert a.this_int_format()=='04'
703
- assert a.this_repr_mode()=='str'
734
+ assert repr(a) == "'C'"
735
+ assert a.this_base() == 36
736
+ assert a.this_int_format() == "04"
737
+ assert a.this_repr_mode() == "str"
704
738
 
705
739
  a = istr(12, int_format="04", repr_mode="str")
706
- assert repr(a)=="'0012'"
707
- assert a.this_base()==10
708
- assert a.this_int_format()=='04'
709
- assert a.this_repr_mode()=='str'
710
-
740
+ assert repr(a) == "'0012'"
741
+ assert a.this_base() == 10
742
+ assert a.this_int_format() == "04"
743
+ assert a.this_repr_mode() == "str"
744
+
745
+
711
746
  def test_digits():
712
747
  assert istr.digits().equals(istr("0123456789"))
713
748
  assert istr.digits("").equals(istr("0123456789"))
@@ -778,12 +813,13 @@ def test_all_distinct():
778
813
 
779
814
 
780
815
  def test_is_consecutive():
781
- assert not istr('').is_consecutive()
782
- assert not istr(1).is_consecutive()
783
- assert istr('abc').is_consecutive()
784
- assert not istr(321).is_consecutive()
785
- assert not istr(11111).is_consecutive()
786
- assert istr.is_consecutive(123)
816
+ assert not istr("").is_consecutive()
817
+ assert not istr(1).is_consecutive()
818
+ assert istr("abc").is_consecutive()
819
+ assert not istr(321).is_consecutive()
820
+ assert not istr(11111).is_consecutive()
821
+ assert istr.is_consecutive(123)
822
+
787
823
 
788
824
  def test_is_triangular():
789
825
  assert istr(1).is_triangular()
@@ -793,7 +829,8 @@ def test_is_triangular():
793
829
  assert istr(6).is_triangular()
794
830
  assert istr(424581).is_triangular()
795
831
  assert not istr(424582).is_triangular()
796
- assert istr.is_triangular(424581)
832
+ assert istr.is_triangular(424581)
833
+
797
834
 
798
835
  def test_prod():
799
836
  assert istr.prod(range(1, 5)).equals(istr(24))
@@ -863,7 +900,7 @@ def test_compose():
863
900
  assert istr("=") == "="
864
901
 
865
902
  assert istr(["=xyz", "=y"]) == [istr(123), istr(2)]
866
-
903
+
867
904
  assert istr(dict(xyz="=xyz", y="=y")) == {"xyz": istr(123), "y": istr(2)}
868
905
  assert istr(dict(xyz="=xyz", y="=y"), namespace=dict(x=3, y=4, z="z")) == {"xyz": istr("34z"), "y": istr(4)}
869
906
 
@@ -872,10 +909,10 @@ def test_compose():
872
909
  assert istr(":=xyz").equals(istr(123))
873
910
  assert xyz.equals(istr(123))
874
911
 
875
- assert istr(":=")==":="
876
- assert istr("=")=="="
877
-
878
-
912
+ assert istr(":=") == ":="
913
+ assert istr("=") == "="
914
+
915
+
879
916
  if __name__ == "__main__":
880
917
  pytest.main(["-vv", "-s", "-x", __file__])
881
918
 
File without changes