istr-python 1.1.41__tar.gz → 1.1.43__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.41
3
+ Version: 1.1.43
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
@@ -589,9 +589,6 @@ map(istr.join,filter(istr.prime, istr.combinations(range(10),2)))
589
589
  ==> istr('02'), istr('03'), ... istr('89')
590
590
  ```
591
591
 
592
- #### version 1.1.30 2026-05-19
593
-
594
-
595
592
  #### reverse an istr
596
593
 
597
594
  The method `reversed()` will return an istr with the reversed content:
@@ -855,16 +852,24 @@ istr.getitem('1234',5, None) ==> None
855
852
  Note that this method has the advantage that it can accept an istr as index, in contrast to normal indexing.
856
853
 
857
854
  #### long square root
858
- The method `long_sqrt` gives a list of the lines of a long square root. Both the result (root) and the original number, possibly with a trailing 0 are returned, along with the calculated values.
859
- E.g. `istr.long_sqrt(123 ** 2)` results in
855
+ The method `long_sqrt` returns an instance of a class with attributes `lines`, `line_lengths` and `output`.
856
+
857
+ `lines` is list of the lines of a long square root. Both the result (root) and the original number, possibly with a trailing 0 are returned, along with the calculated values.
858
+ E.g. `istr.long_sqrt(123 ** 2).lines` results in
860
859
 
861
860
  `[istr('123'), istr('15129'), istr('1'), istr('51'), istr('44'), istr('729'), istr('729'), istr('0')]`
862
861
 
863
862
  Note the final `istr('0')` is included to support also long square roots that are not perfect.
864
863
 
865
- The method `long_sqrt`has an optional parameter `as_str`, that can be used to get a nice string representation of the long square root lines.
864
+ `line_lengths` returns a list of the length of the lines (not istr-ed). E.g.:
865
+
866
+ E.g. `istr.long_sqrt(123 ** 2).line_lengths` results in
866
867
 
867
- E.g. `print(istr.long_sqrt(123 ** 2, as_str=True))` will print
868
+ `[3, 5, 1, 2, 2, 3, 3, 1]`
869
+
870
+ `output` can be used to get a nice string representation of the long square root lines.
871
+
872
+ E.g. `print(istr.long_sqrt(123 ** 2).output)` will print
868
873
  ```
869
874
  1 2 3
870
875
  -----
@@ -882,18 +887,26 @@ E.g. `print(istr.long_sqrt(123 ** 2, as_str=True))` will print
882
887
 
883
888
  #### long multiplication
884
889
 
885
- The method `long_multiplication` gives a list of all lines to do a long multiplition.
890
+ The method `long_multiplication` returns an instance of a class with attributes `lines`, `line_lengths` and `output`.
891
+
892
+ `lines` is list of the lines of a long multiplication.
893
+
894
+ E.g. `print(istr.long_multiplication(1234,567).lines` will print
886
895
 
887
- E.g. `print(istr.long_multiplication(1234,567)` will print
888
-
889
896
  `[istr('1234'), istr('567'), istr('8638'), istr('7404'), istr('6170'), istr('699678')]`
890
-
891
- The method has an optional parameter `as_str`, that can be used t0 get a nice representation of the long multiplication lines.
892
-
893
- E.g. `print(istr.long_multiplication(1234,567, as_str=True)` will print
897
+
898
+ `line_lengths` returns a list of the length of the lines (not istr-ed). E.g.:
899
+
900
+ E. g. `print(istr.long_multiplication(1234,567).line_lengths` results in
901
+
902
+ `[4, 3, 4, 4, 4, 6]`
903
+
904
+ `output` can be used to get a nice string representation of the long multiplication lines.
905
+
906
+ E.g. `print(istr.long_multiplication(1234, 567).output)` will print
894
907
 
895
908
  ```
896
- 1234
909
+ 1234
897
910
  567
898
911
  ------ x
899
912
  8638
@@ -905,15 +918,23 @@ E.g. `print(istr.long_multiplication(1234,567, as_str=True)` will print
905
918
 
906
919
  #### long division
907
920
 
908
- The method `long_division` gives a list of all lines to do a long division (note that the divisor and dividend count as separate lines).
921
+ The method `long_division` returns an instance of a class with attributes `lines`, `line_lengths` and `output`.
909
922
 
910
- E.g. `print(istr.long_division(1395, 45)` will print
923
+ `lines` is a list of the lines of a long division.
911
924
 
912
- [istr('31'), istr('45'), istr('1395'), istr('135'), istr('45'), istr('45'), istr('0')]
913
-
914
- The method has an optional parameter `as_str`, that can be used to get a nice representation of the long multiplication lines.
915
-
916
- E.g. `print(istr.long_division(1395, 45, as_str=True)` will print
925
+ E.g. `print(istr.long_division(1395, 45).lines` will print
926
+
927
+ `[istr('31'), istr('45'), istr('1395'), istr('135'), istr('45'), istr('45'), istr('0')]`
928
+
929
+ Note the final `istr('0')` is included to support also long divisions roots that are not perfect.
930
+
931
+ E.g. `print(istr.long_division(1395, 45).line_lengths` will print
932
+
933
+ `[2, 2, 4, 3, 2, 2, 1]`
934
+
935
+ `output` can be used to get a nice string representation of the long divsion lines.
936
+
937
+ E.g. `print(istr.long_division(1395, 45).output` will print
917
938
 
918
939
  ```
919
940
  31
@@ -925,8 +946,8 @@ E.g. `print(istr.long_division(1395, 45, as_str=True)` will print
925
946
  45
926
947
  --
927
948
  0
928
- ```
929
-
949
+ ```
950
+
930
951
  #### generate istr with digits
931
952
 
932
953
  The class method `digits` can be used to return an istr of digits according to a given specification.
@@ -576,9 +576,6 @@ map(istr.join,filter(istr.prime, istr.combinations(range(10),2)))
576
576
  ==> istr('02'), istr('03'), ... istr('89')
577
577
  ```
578
578
 
579
- #### version 1.1.30 2026-05-19
580
-
581
-
582
579
  #### reverse an istr
583
580
 
584
581
  The method `reversed()` will return an istr with the reversed content:
@@ -842,16 +839,24 @@ istr.getitem('1234',5, None) ==> None
842
839
  Note that this method has the advantage that it can accept an istr as index, in contrast to normal indexing.
843
840
 
844
841
  #### long square root
845
- The method `long_sqrt` gives a list of the lines of a long square root. Both the result (root) and the original number, possibly with a trailing 0 are returned, along with the calculated values.
846
- E.g. `istr.long_sqrt(123 ** 2)` results in
842
+ The method `long_sqrt` returns an instance of a class with attributes `lines`, `line_lengths` and `output`.
843
+
844
+ `lines` is list of the lines of a long square root. Both the result (root) and the original number, possibly with a trailing 0 are returned, along with the calculated values.
845
+ E.g. `istr.long_sqrt(123 ** 2).lines` results in
847
846
 
848
847
  `[istr('123'), istr('15129'), istr('1'), istr('51'), istr('44'), istr('729'), istr('729'), istr('0')]`
849
848
 
850
849
  Note the final `istr('0')` is included to support also long square roots that are not perfect.
851
850
 
852
- The method `long_sqrt`has an optional parameter `as_str`, that can be used to get a nice string representation of the long square root lines.
851
+ `line_lengths` returns a list of the length of the lines (not istr-ed). E.g.:
852
+
853
+ E.g. `istr.long_sqrt(123 ** 2).line_lengths` results in
853
854
 
854
- E.g. `print(istr.long_sqrt(123 ** 2, as_str=True))` will print
855
+ `[3, 5, 1, 2, 2, 3, 3, 1]`
856
+
857
+ `output` can be used to get a nice string representation of the long square root lines.
858
+
859
+ E.g. `print(istr.long_sqrt(123 ** 2).output)` will print
855
860
  ```
856
861
  1 2 3
857
862
  -----
@@ -869,18 +874,26 @@ E.g. `print(istr.long_sqrt(123 ** 2, as_str=True))` will print
869
874
 
870
875
  #### long multiplication
871
876
 
872
- The method `long_multiplication` gives a list of all lines to do a long multiplition.
877
+ The method `long_multiplication` returns an instance of a class with attributes `lines`, `line_lengths` and `output`.
878
+
879
+ `lines` is list of the lines of a long multiplication.
880
+
881
+ E.g. `print(istr.long_multiplication(1234,567).lines` will print
873
882
 
874
- E.g. `print(istr.long_multiplication(1234,567)` will print
875
-
876
883
  `[istr('1234'), istr('567'), istr('8638'), istr('7404'), istr('6170'), istr('699678')]`
877
-
878
- The method has an optional parameter `as_str`, that can be used t0 get a nice representation of the long multiplication lines.
879
-
880
- E.g. `print(istr.long_multiplication(1234,567, as_str=True)` will print
884
+
885
+ `line_lengths` returns a list of the length of the lines (not istr-ed). E.g.:
886
+
887
+ E. g. `print(istr.long_multiplication(1234,567).line_lengths` results in
888
+
889
+ `[4, 3, 4, 4, 4, 6]`
890
+
891
+ `output` can be used to get a nice string representation of the long multiplication lines.
892
+
893
+ E.g. `print(istr.long_multiplication(1234, 567).output)` will print
881
894
 
882
895
  ```
883
- 1234
896
+ 1234
884
897
  567
885
898
  ------ x
886
899
  8638
@@ -892,15 +905,23 @@ E.g. `print(istr.long_multiplication(1234,567, as_str=True)` will print
892
905
 
893
906
  #### long division
894
907
 
895
- The method `long_division` gives a list of all lines to do a long division (note that the divisor and dividend count as separate lines).
908
+ The method `long_division` returns an instance of a class with attributes `lines`, `line_lengths` and `output`.
896
909
 
897
- E.g. `print(istr.long_division(1395, 45)` will print
910
+ `lines` is a list of the lines of a long division.
898
911
 
899
- [istr('31'), istr('45'), istr('1395'), istr('135'), istr('45'), istr('45'), istr('0')]
900
-
901
- The method has an optional parameter `as_str`, that can be used to get a nice representation of the long multiplication lines.
902
-
903
- E.g. `print(istr.long_division(1395, 45, as_str=True)` will print
912
+ E.g. `print(istr.long_division(1395, 45).lines` will print
913
+
914
+ `[istr('31'), istr('45'), istr('1395'), istr('135'), istr('45'), istr('45'), istr('0')]`
915
+
916
+ Note the final `istr('0')` is included to support also long divisions roots that are not perfect.
917
+
918
+ E.g. `print(istr.long_division(1395, 45).line_lengths` will print
919
+
920
+ `[2, 2, 4, 3, 2, 2, 1]`
921
+
922
+ `output` can be used to get a nice string representation of the long divsion lines.
923
+
924
+ E.g. `print(istr.long_division(1395, 45).output` will print
904
925
 
905
926
  ```
906
927
  31
@@ -912,8 +933,8 @@ E.g. `print(istr.long_division(1395, 45, as_str=True)` will print
912
933
  45
913
934
  --
914
935
  0
915
- ```
916
-
936
+ ```
937
+
917
938
  #### generate istr with digits
918
939
 
919
940
  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.41"
8
+ __version__ = "1.1.43"
9
9
  import functools
10
10
  import itertools
11
11
  import types
@@ -727,19 +727,16 @@ class istr(str):
727
727
  return self[::-1]
728
728
 
729
729
  def ceil(self, divisible_by=1):
730
- if divisible_by <= 0:
731
- raise ValueError(f"step has to be >0, not {divisible_by}")
732
- if divisible_by != int(divisible_by):
733
- raise ValueError(f"step has to be an integer value, not {divisible_by}")
730
+ if divisible_by != int(divisible_by) or divisible_by < 1:
731
+ raise ValueError(f"divisible_by has to be an integer value >=1 , not {divisible_by}")
734
732
  n = istr.interpret_as_float(self)
735
733
  return istr((math.ceil(n / divisible_by)) * divisible_by)
736
734
 
737
735
  def floor(self, divisible_by=1):
738
- if divisible_by <= 0:
739
- raise ValueError(f"step has to be >0, not {divisible_by}")
740
- if divisible_by != int(divisible_by):
741
- raise ValueError(f"step has to be an integer value, not {divisible_by}")
742
- return istr((math.floor(self / divisible_by)) * divisible_by)
736
+ if divisible_by != int(divisible_by) or divisible_by < 1:
737
+ raise ValueError(f"divisible_by has to be an integer value >=1 , not {divisible_by}")
738
+ n = istr.interpret_as_float(self)
739
+ return istr((math.floor(n / divisible_by)) * divisible_by)
743
740
 
744
741
  def interpret_as_int(self):
745
742
  if isinstance(self, istr):
@@ -767,13 +764,13 @@ class istr(str):
767
764
 
768
765
  return str(self)
769
766
 
770
- def interpret_as_istr(self):
767
+ def interpret_as_istr(self):
771
768
  # ignore leading zeroes
772
769
  if isinstance(self, collections.abc.Iterable) and not isinstance(self, str):
773
- return istr.join(self)*1
770
+ return istr.join(self) * 1
771
+
772
+ return istr(self) * 1
774
773
 
775
- return istr(self)*1
776
-
777
774
  def _str_method(self, name, *args, **kwargs):
778
775
  return self.__class__(getattr(super(), name)(*args, **kwargs))
779
776
 
@@ -863,22 +860,15 @@ class istr(str):
863
860
  for i, value in enumerate(iterable, int(start)):
864
861
  yield cls(i), value
865
862
 
866
- def long_sqrt(self, as_str=False):
867
- """
868
- this function does long squre root and returns all the lines to be written.
869
- (modified from a ChatGPT code suggestion)
870
- returns root, number and the lines below the number, all istr-ed
871
- if as_str is True, a formatted output will be returned
872
- """
873
-
874
- number = istr.interpret_as_istr(self)
875
-
863
+ @staticmethod
864
+ @functools.lru_cache(maxsize=1)
865
+ def _long_sqrt(number):
876
866
  lines = []
877
- if len(number)%2==1:
878
- pairs=istr.chain(number[0],istr.batched(number[1:],2,join=True))
879
- else:
880
- pairs=istr.batched(number,2,join=True)
881
-
867
+ if len(number) % 2 == 1:
868
+ pairs = istr.chain(number[0], istr.batched(number[1:], 2, join=True))
869
+ else:
870
+ pairs = istr.batched(number, 2, join=True)
871
+
882
872
  root = istr(0)
883
873
  remainder = 0
884
874
 
@@ -896,123 +886,186 @@ class istr(str):
896
886
  remainder = dividend - trial
897
887
 
898
888
  root = root * 10 + x
899
- lines.append(dividend)
900
- lines.append(trial)
889
+ if trial:
890
+ lines.append(dividend)
891
+ lines.append(trial)
901
892
 
902
893
  lines.append(remainder)
903
894
  lines = [root] + [number] + lines[1:]
904
- if as_str:
905
- result = []
906
- extra = len(number)%2==0
907
- result.append(f" {extra*' '}{' '.join(lines[0])}")
908
- result.append(f" {len(lines[1])*'-'}")
909
- result.append(f"\/{lines[1]}")
910
-
911
- last_len=len(lines[2])
912
- for i, line in enumerate(lines):
913
-
914
- if i >= 2:
915
- if i == len(lines) - 1:
916
- n = i - 2 + extra
917
- else:
918
- n = i + extra
895
+ return lines
919
896
 
920
- if i % 2 == 0:
921
- result.append(f" {line:>{n}}")
922
- result.append(f" {last_len*'-':>{n}}")
923
- else:
924
- result.append(f" {line:>{n+1}}")
925
- last_len=len(line)
926
- return "\n".join(result)
927
- else:
928
- return lines
929
-
930
- def long_multiplication(self, number,as_str=False):
931
- number0 = istr.interpret_as_istr(self)
932
- number1=istr.interpret_as_istr(number)
933
- lines=[number0,number1]
897
+ def long_sqrt(self):
898
+ """
899
+ this function does long squre root and returns all the lines to be written.
900
+ (modified from a ChatGPT code suggestion)
901
+ returns root, number and the lines below the number, all istr-ed
902
+ if as_str is True, a formatted output will be returned
903
+ """
904
+
905
+ class NumberHolder:
906
+ def __init__(self, number):
907
+ self.number = number
908
+
909
+ def __getattr__(self, attr):
910
+ match attr:
911
+ case "lines":
912
+ return istr._long_sqrt(self.number)
913
+ case "line_lengths":
914
+ return [*map(len, istr._long_sqrt(self.number))]
915
+ case "output":
916
+ lines = istr._long_sqrt(self.number)
917
+ result = []
918
+ extra = len(number) % 2 == 0
919
+ result.append(f" {extra*' '}{' '.join(lines[0])}")
920
+ result.append(f" {len(lines[1])*'-'}")
921
+ result.append(f"\/{lines[1]}")
922
+
923
+ last_len = len(lines[2])
924
+ for i, line in enumerate(lines):
925
+ if i >= 2:
926
+ if i == len(lines) - 1:
927
+ n = i - 2 + extra
928
+ else:
929
+ n = i + extra
930
+
931
+ if i % 2 == 0:
932
+ result.append(f" {line:>{n}}")
933
+ result.append(f" {last_len*'-':>{n}}")
934
+ else:
935
+ result.append(f" {line:>{n+1}}")
936
+ last_len = len(line)
937
+ return "\n".join(result)
938
+ case _:
939
+ raise AttributeError()
940
+
941
+ number = istr.interpret_as_istr(self)
942
+
943
+ return NumberHolder(number)
944
+
945
+ @staticmethod
946
+ @functools.lru_cache(maxsize=1)
947
+ def _long_multiplication(number0, number1):
948
+ lines = [number0, number1]
934
949
  for i in number1.reversed():
935
- lines.append(number0*i)
936
- lines.append(number0*number1)
937
- if as_str:
938
- result=[]
939
- n=len(lines[-1])
940
- result.append(f'{lines[0]:>{n}}')
941
- result.append(f'{lines[1]:>{n}}')
942
- result.append(f"{n*'-'} x")
943
- for i,line in enumerate(lines[2:-1]):
944
- result.append(f"{line:>{n-i}}")
945
- result.append(f"{n*'-'}")
946
- result.append(lines[-1])
947
- return '\n'.join(result)
948
- else:
949
- return lines
950
-
951
- def long_division(self, divisor, as_str=False):
952
- dividend = istr.interpret_as_istr(self)
953
- divisor = istr.interpret_as_istr(divisor)
954
-
950
+ lines.append(number0 * i)
951
+ lines.append(number0 * number1)
952
+
953
+ return lines
954
+
955
+ def long_multiplication(self, number, as_str=False):
956
+ class NumbersHolder:
957
+ def __init__(self, number0, number1):
958
+ self.number0 = number0
959
+ self.number1 = number1
960
+
961
+ def __getattr__(self, attr):
962
+ match attr:
963
+ case "lines":
964
+ return istr._long_multiplication(self.number0, self.number1)
965
+ case "line_lengths":
966
+ return [*map(len, istr._long_multiplication(self.number0, self.number1))]
967
+ case "output":
968
+ lines = istr._long_multiplication(self.number0, self.number1)
969
+ result = []
970
+
971
+ n = len(lines[-1])
972
+ result.append(f"{lines[0]:>{n}}")
973
+ result.append(f"{lines[1]:>{n}}")
974
+ result.append(f"{n*'-'} x")
975
+ for i, line in enumerate(lines[2:-1]):
976
+ result.append(f"{line:>{n-i}}")
977
+ result.append(f"{n*'-'}")
978
+ result.append(lines[-1])
979
+ return "\n".join(result)
980
+ case _:
981
+ raise AttributeError()
982
+
983
+ number0 = istr.interpret_as_istr(self)
984
+ number1 = istr.interpret_as_istr(number)
985
+ return NumbersHolder(number0, number1)
986
+
987
+ @staticmethod
988
+ @functools.lru_cache(maxsize=1)
989
+ def _long_division(dividend, divisor, as_str=False):
955
990
  if dividend < 0:
956
991
  raise ValueError("dividend must be non-negative.")
957
-
992
+
958
993
  if divisor <= 0:
959
994
  raise ValueError("divisor must be positive.")
960
-
995
+
961
996
  dividend_start = len(divisor) + 3
962
997
  total_width = dividend_start + len(dividend)
963
-
998
+
964
999
  quotient_characters = [" "] * len(dividend)
965
-
1000
+
966
1001
  remainder = 0
967
1002
  division_started = False
968
- lines=[]
969
-
1003
+ lines = []
1004
+
970
1005
  for digit_index, digit_character in enumerate(dividend):
971
1006
  current = remainder * 10 + digit_character
972
1007
  quotient_digit = current // divisor
973
-
1008
+
974
1009
  if quotient_digit != 0 or division_started:
975
1010
  division_started = True
976
-
1011
+
977
1012
  if digit_index == len(dividend) - 1:
978
1013
  division_started = True
979
-
1014
+
980
1015
  if not division_started:
981
1016
  remainder = current
982
1017
  continue
983
-
1018
+
984
1019
  quotient_characters[digit_index] = quotient_digit
985
-
1020
+
986
1021
  product = quotient_digit * divisor
987
1022
  remainder = current - product
988
-
1023
+
989
1024
  if as_str:
990
1025
  end_column = dividend_start + digit_index
991
1026
  current_start = end_column - len(current) + 1
992
- product_start = end_column - len(product) + 1
1027
+ product_start = end_column - len(product) + 1
993
1028
  bar_start = min(current_start, product_start)
994
1029
  bar_length = end_column - bar_start + 1
995
1030
  lines.append(" " * current_start | current)
996
1031
  lines.append(" " * product_start | product)
997
1032
  lines.append(" " * bar_start + "-" * bar_length)
998
-
1033
+
999
1034
  else:
1000
1035
  lines.append(current)
1001
1036
  lines.append(product)
1002
-
1037
+
1003
1038
  quotient = istr.join(quotient_characters).rstrip()
1004
1039
  if as_str:
1005
1040
  remainder_start = total_width - len(remainder)
1006
1041
  lines.append(" " * remainder_start | remainder)
1007
- lines=[" " * dividend_start | quotient," " * dividend_start + "-" * len(dividend),f"{divisor} ) {dividend}",*lines[1:]]
1042
+ lines = [" " * dividend_start | quotient, " " * dividend_start + "-" * len(dividend), f"{divisor} ) {dividend}", *lines[1:]]
1008
1043
  return "\n".join(lines)
1009
-
1044
+
1010
1045
  else:
1011
1046
  lines.append(remainder)
1012
- lines=[istr(quotient),divisor,dividend,*lines[1:]]
1013
- return lines
1014
-
1015
-
1047
+ lines = [istr(quotient), divisor, dividend, *lines[1:]]
1048
+ return lines
1049
+
1050
+ def long_division(self, divisor):
1051
+ class NumbersHolder:
1052
+ def __init__(self, dividend, divisor):
1053
+ self.dividend = dividend
1054
+ self.divisor = divisor
1055
+
1056
+ def __getattr__(self, attr):
1057
+ match attr:
1058
+ case "lines":
1059
+ return istr._long_division(self.dividend, self.divisor)
1060
+ case "line_lengths":
1061
+ return [*map(len, istr._long_division(self.dividend, self.divisor))]
1062
+ case "output":
1063
+ return istr._long_division(self.dividend, self.divisor, as_str=True)
1064
+
1065
+ dividend = istr.interpret_as_istr(self)
1066
+ divisor = istr.interpret_as_istr(divisor)
1067
+ return NumbersHolder(dividend, divisor)
1068
+
1016
1069
  def this_base(self):
1017
1070
  return self._this_base
1018
1071