vedicmaths 0.1.0__py3-none-any.whl
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.
- vedicmathematics/__init__.py +107 -0
- vedicmathematics/arithmetic.py +683 -0
- vedicmaths/__init__.py +4 -0
- vedicmaths-0.1.0.dist-info/METADATA +206 -0
- vedicmaths-0.1.0.dist-info/RECORD +8 -0
- vedicmaths-0.1.0.dist-info/WHEEL +5 -0
- vedicmaths-0.1.0.dist-info/licenses/LICENSE +21 -0
- vedicmaths-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Vedic mathematics helpers with optional step-by-step workings."""
|
|
2
|
+
|
|
3
|
+
from .arithmetic import (
|
|
4
|
+
OperationTrace,
|
|
5
|
+
Step,
|
|
6
|
+
all_from_9_last_from_10,
|
|
7
|
+
anurupyena_multiply,
|
|
8
|
+
anurupyena_steps,
|
|
9
|
+
casting_out_nines_check,
|
|
10
|
+
complement,
|
|
11
|
+
cube_ending_in_5,
|
|
12
|
+
cube_root_if_perfect,
|
|
13
|
+
digit_sum,
|
|
14
|
+
digital_root,
|
|
15
|
+
divide_by_5,
|
|
16
|
+
divide_by_25,
|
|
17
|
+
fraction_to_percent,
|
|
18
|
+
is_divisible_by_3,
|
|
19
|
+
is_divisible_by_9,
|
|
20
|
+
is_divisible_by_11,
|
|
21
|
+
left_to_right_add,
|
|
22
|
+
left_to_right_add_steps,
|
|
23
|
+
multiply_by_5,
|
|
24
|
+
multiply_by_9,
|
|
25
|
+
multiply_by_11,
|
|
26
|
+
multiply_by_11_steps,
|
|
27
|
+
multiply_by_12,
|
|
28
|
+
multiply_by_15,
|
|
29
|
+
multiply_by_25,
|
|
30
|
+
multiply_by_50,
|
|
31
|
+
multiply_by_75,
|
|
32
|
+
multiply_by_99,
|
|
33
|
+
multiply_by_125,
|
|
34
|
+
multiply_by_999,
|
|
35
|
+
multiply_by_repeated_9,
|
|
36
|
+
nikhilam_multiply,
|
|
37
|
+
nikhilam_steps,
|
|
38
|
+
osculator_for_ending_9,
|
|
39
|
+
percent_of,
|
|
40
|
+
recurring_decimal_unit_fraction,
|
|
41
|
+
square_ending_in_5,
|
|
42
|
+
square_ending_in_5_steps,
|
|
43
|
+
square_near_50,
|
|
44
|
+
square_near_base,
|
|
45
|
+
square_near_base_steps,
|
|
46
|
+
square_root_if_perfect,
|
|
47
|
+
subtract_using_complement,
|
|
48
|
+
subtract_using_complement_steps,
|
|
49
|
+
sum_digits_until_single,
|
|
50
|
+
transpose_percent,
|
|
51
|
+
urdhva_tiryagbhyam_multiply,
|
|
52
|
+
urdhva_tiryagbhyam_steps,
|
|
53
|
+
vinculum_digits,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"OperationTrace",
|
|
58
|
+
"Step",
|
|
59
|
+
"all_from_9_last_from_10",
|
|
60
|
+
"anurupyena_multiply",
|
|
61
|
+
"anurupyena_steps",
|
|
62
|
+
"casting_out_nines_check",
|
|
63
|
+
"complement",
|
|
64
|
+
"cube_ending_in_5",
|
|
65
|
+
"cube_root_if_perfect",
|
|
66
|
+
"digit_sum",
|
|
67
|
+
"digital_root",
|
|
68
|
+
"divide_by_5",
|
|
69
|
+
"divide_by_25",
|
|
70
|
+
"fraction_to_percent",
|
|
71
|
+
"is_divisible_by_3",
|
|
72
|
+
"is_divisible_by_9",
|
|
73
|
+
"is_divisible_by_11",
|
|
74
|
+
"left_to_right_add",
|
|
75
|
+
"left_to_right_add_steps",
|
|
76
|
+
"multiply_by_5",
|
|
77
|
+
"multiply_by_9",
|
|
78
|
+
"multiply_by_11",
|
|
79
|
+
"multiply_by_11_steps",
|
|
80
|
+
"multiply_by_12",
|
|
81
|
+
"multiply_by_15",
|
|
82
|
+
"multiply_by_25",
|
|
83
|
+
"multiply_by_50",
|
|
84
|
+
"multiply_by_75",
|
|
85
|
+
"multiply_by_99",
|
|
86
|
+
"multiply_by_125",
|
|
87
|
+
"multiply_by_999",
|
|
88
|
+
"multiply_by_repeated_9",
|
|
89
|
+
"nikhilam_multiply",
|
|
90
|
+
"nikhilam_steps",
|
|
91
|
+
"osculator_for_ending_9",
|
|
92
|
+
"percent_of",
|
|
93
|
+
"recurring_decimal_unit_fraction",
|
|
94
|
+
"square_ending_in_5",
|
|
95
|
+
"square_ending_in_5_steps",
|
|
96
|
+
"square_near_50",
|
|
97
|
+
"square_near_base",
|
|
98
|
+
"square_near_base_steps",
|
|
99
|
+
"square_root_if_perfect",
|
|
100
|
+
"subtract_using_complement",
|
|
101
|
+
"subtract_using_complement_steps",
|
|
102
|
+
"sum_digits_until_single",
|
|
103
|
+
"transpose_percent",
|
|
104
|
+
"urdhva_tiryagbhyam_multiply",
|
|
105
|
+
"urdhva_tiryagbhyam_steps",
|
|
106
|
+
"vinculum_digits",
|
|
107
|
+
]
|
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
"""Arithmetic techniques inspired by common Vedic mathematics methods.
|
|
2
|
+
|
|
3
|
+
The functions return plain integer answers by default. Companion ``*_steps``
|
|
4
|
+
functions return a trace that can be rendered in notebooks, CLIs, or teaching
|
|
5
|
+
apps.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class Step:
|
|
15
|
+
"""One human-readable step in a worked calculation."""
|
|
16
|
+
|
|
17
|
+
title: str
|
|
18
|
+
expression: str
|
|
19
|
+
value: int | str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class OperationTrace:
|
|
24
|
+
"""A worked calculation result."""
|
|
25
|
+
|
|
26
|
+
method: str
|
|
27
|
+
inputs: tuple[int, ...]
|
|
28
|
+
result: int
|
|
29
|
+
steps: tuple[Step, ...]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _sign(value: int) -> int:
|
|
33
|
+
return -1 if value < 0 else 1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _digits_reversed(value: int) -> list[int]:
|
|
37
|
+
return [int(char) for char in reversed(str(abs(value)))]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _require_non_negative(value: int, name: str) -> None:
|
|
41
|
+
if value < 0:
|
|
42
|
+
raise ValueError(f"{name} must be non-negative")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _nearest_power_of_ten(value: int) -> int:
|
|
46
|
+
if value == 0:
|
|
47
|
+
return 10
|
|
48
|
+
digits = len(str(abs(value)))
|
|
49
|
+
lower = 10 ** (digits - 1)
|
|
50
|
+
upper = 10**digits
|
|
51
|
+
return lower if abs(value - lower) < abs(value - upper) else upper
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def digital_root(number: int) -> int:
|
|
55
|
+
"""Return the digital root of an integer.
|
|
56
|
+
|
|
57
|
+
``digital_root(0)`` is ``0``. Negative values are treated by magnitude,
|
|
58
|
+
which keeps the helper useful for arithmetic verification.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
number = abs(number)
|
|
62
|
+
if number == 0:
|
|
63
|
+
return 0
|
|
64
|
+
return 1 + ((number - 1) % 9)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def digit_sum(number: int) -> int:
|
|
68
|
+
"""Return the sum of decimal digits in ``number``."""
|
|
69
|
+
|
|
70
|
+
return sum(int(char) for char in str(abs(number)))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def sum_digits_until_single(number: int) -> int:
|
|
74
|
+
"""Repeatedly sum digits until a single digit remains."""
|
|
75
|
+
|
|
76
|
+
return digital_root(number)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def complement(number: int, base: int | None = None) -> int:
|
|
80
|
+
"""Return the complement of ``number`` from a base.
|
|
81
|
+
|
|
82
|
+
If no base is supplied, the next power of ten is used. For example,
|
|
83
|
+
``complement(87)`` returns ``13`` because ``100 - 87 = 13``.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
_require_non_negative(number, "number")
|
|
87
|
+
if base is None:
|
|
88
|
+
base = 10 ** len(str(number))
|
|
89
|
+
if base <= number:
|
|
90
|
+
raise ValueError("base must be greater than number")
|
|
91
|
+
return base - number
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def all_from_9_last_from_10(number: int) -> int:
|
|
95
|
+
"""Return the Nikhilam complement from the next power of ten."""
|
|
96
|
+
|
|
97
|
+
return complement(number)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def left_to_right_add(a: int, b: int) -> int:
|
|
101
|
+
"""Add two non-negative integers from left to right."""
|
|
102
|
+
|
|
103
|
+
return left_to_right_add_steps(a, b).result
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def left_to_right_add_steps(a: int, b: int) -> OperationTrace:
|
|
107
|
+
"""Return a trace for left-to-right addition with carries."""
|
|
108
|
+
|
|
109
|
+
_require_non_negative(a, "a")
|
|
110
|
+
_require_non_negative(b, "b")
|
|
111
|
+
width = max(len(str(a)), len(str(b)))
|
|
112
|
+
left = str(a).zfill(width)
|
|
113
|
+
right = str(b).zfill(width)
|
|
114
|
+
carry = 0
|
|
115
|
+
digits: list[str] = []
|
|
116
|
+
steps: list[Step] = []
|
|
117
|
+
|
|
118
|
+
for index in range(width - 1, -1, -1):
|
|
119
|
+
total = int(left[index]) + int(right[index]) + carry
|
|
120
|
+
digits.append(str(total % 10))
|
|
121
|
+
steps.append(
|
|
122
|
+
Step(
|
|
123
|
+
f"Column {width - index}",
|
|
124
|
+
f"{left[index]} + {right[index]} + carry {carry}",
|
|
125
|
+
f"write {total % 10}, carry {total // 10}",
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
carry = total // 10
|
|
129
|
+
if carry:
|
|
130
|
+
digits.append(str(carry))
|
|
131
|
+
steps.append(Step("Final carry", str(carry), carry))
|
|
132
|
+
|
|
133
|
+
result = int("".join(reversed(digits)))
|
|
134
|
+
return OperationTrace(
|
|
135
|
+
method="Left-to-right addition",
|
|
136
|
+
inputs=(a, b),
|
|
137
|
+
result=result,
|
|
138
|
+
steps=tuple(reversed(steps)),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def subtract_using_complement(a: int, b: int) -> int:
|
|
143
|
+
"""Subtract ``b`` from ``a`` using complements."""
|
|
144
|
+
|
|
145
|
+
return subtract_using_complement_steps(a, b).result
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def subtract_using_complement_steps(a: int, b: int) -> OperationTrace:
|
|
149
|
+
"""Return a trace for subtraction by complement."""
|
|
150
|
+
|
|
151
|
+
_require_non_negative(a, "a")
|
|
152
|
+
_require_non_negative(b, "b")
|
|
153
|
+
if b > a:
|
|
154
|
+
trace = subtract_using_complement_steps(b, a)
|
|
155
|
+
return OperationTrace(
|
|
156
|
+
method="Subtraction by complement",
|
|
157
|
+
inputs=(a, b),
|
|
158
|
+
result=-trace.result,
|
|
159
|
+
steps=trace.steps + (Step("Apply sign", f"{a} - {b}", -trace.result),),
|
|
160
|
+
)
|
|
161
|
+
base = 10 ** max(len(str(a)), len(str(b)))
|
|
162
|
+
comp = base - b
|
|
163
|
+
raw = a + comp
|
|
164
|
+
result = raw - base
|
|
165
|
+
return OperationTrace(
|
|
166
|
+
method="Subtraction by complement",
|
|
167
|
+
inputs=(a, b),
|
|
168
|
+
result=result,
|
|
169
|
+
steps=(
|
|
170
|
+
Step("Choose base", f"base = {base}", base),
|
|
171
|
+
Step("Find complement", f"{base} - {b}", comp),
|
|
172
|
+
Step("Add complement", f"{a} + {comp}", raw),
|
|
173
|
+
Step("Drop base", f"{raw} - {base}", result),
|
|
174
|
+
),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def vinculum_digits(number: int) -> tuple[int, ...]:
|
|
179
|
+
"""Represent a number with vinculum-style signed digits.
|
|
180
|
+
|
|
181
|
+
Digits greater than 5 are replaced by their negative complement and a carry
|
|
182
|
+
is moved left. The tuple is ordered from most significant to least.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
_require_non_negative(number, "number")
|
|
186
|
+
digits = _digits_reversed(number)
|
|
187
|
+
result: list[int] = []
|
|
188
|
+
carry = 0
|
|
189
|
+
for digit in digits:
|
|
190
|
+
value = digit + carry
|
|
191
|
+
if value > 5:
|
|
192
|
+
result.append(value - 10)
|
|
193
|
+
carry = 1
|
|
194
|
+
else:
|
|
195
|
+
result.append(value)
|
|
196
|
+
carry = 0
|
|
197
|
+
if carry:
|
|
198
|
+
result.append(carry)
|
|
199
|
+
return tuple(reversed(result))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def multiply_by_9(number: int) -> int:
|
|
203
|
+
"""Multiply by 9 using ``10n - n``."""
|
|
204
|
+
|
|
205
|
+
return number * 10 - number
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def multiply_by_99(number: int) -> int:
|
|
209
|
+
"""Multiply by 99 using ``100n - n``."""
|
|
210
|
+
|
|
211
|
+
return number * 100 - number
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def multiply_by_999(number: int) -> int:
|
|
215
|
+
"""Multiply by 999 using ``1000n - n``."""
|
|
216
|
+
|
|
217
|
+
return number * 1000 - number
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def multiply_by_repeated_9(number: int, count: int) -> int:
|
|
221
|
+
"""Multiply by a number made of ``count`` nines, such as 9, 99, or 999."""
|
|
222
|
+
|
|
223
|
+
if count <= 0:
|
|
224
|
+
raise ValueError("count must be positive")
|
|
225
|
+
return number * ((10**count) - 1)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def multiply_by_11(number: int) -> int:
|
|
229
|
+
"""Multiply by 11 using the adjacent-sum shortcut."""
|
|
230
|
+
|
|
231
|
+
return multiply_by_11_steps(number).result
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def multiply_by_11_steps(number: int) -> OperationTrace:
|
|
235
|
+
"""Return a trace for multiplying by 11."""
|
|
236
|
+
|
|
237
|
+
sign = _sign(number)
|
|
238
|
+
digits = [int(char) for char in str(abs(number))]
|
|
239
|
+
work = [digits[0]]
|
|
240
|
+
steps = [Step("First digit", str(digits[0]), digits[0])]
|
|
241
|
+
for left, right in zip(digits, digits[1:]):
|
|
242
|
+
total = left + right
|
|
243
|
+
work.append(total)
|
|
244
|
+
steps.append(Step("Adjacent sum", f"{left} + {right}", total))
|
|
245
|
+
if len(digits) > 1:
|
|
246
|
+
work.append(digits[-1])
|
|
247
|
+
steps.append(Step("Last digit", str(digits[-1]), digits[-1]))
|
|
248
|
+
carry = 0
|
|
249
|
+
output: list[int] = []
|
|
250
|
+
for value in reversed(work):
|
|
251
|
+
total = value + carry
|
|
252
|
+
output.append(total % 10)
|
|
253
|
+
carry = total // 10
|
|
254
|
+
while carry:
|
|
255
|
+
output.append(carry % 10)
|
|
256
|
+
carry //= 10
|
|
257
|
+
result = int("".join(str(digit) for digit in reversed(output))) * sign
|
|
258
|
+
return OperationTrace(
|
|
259
|
+
method="Multiply by 11 using adjacent sums",
|
|
260
|
+
inputs=(number,),
|
|
261
|
+
result=result,
|
|
262
|
+
steps=tuple(steps + [Step("Carry and combine", str(work), result)]),
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def multiply_by_12(number: int) -> int:
|
|
267
|
+
"""Multiply by 12 as ``10n + 2n``."""
|
|
268
|
+
|
|
269
|
+
return number * 10 + number * 2
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def multiply_by_15(number: int) -> int:
|
|
273
|
+
"""Multiply by 15 as ``10n + half of 10n``."""
|
|
274
|
+
|
|
275
|
+
return number * 10 + number * 5
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def multiply_by_25(number: int) -> int:
|
|
279
|
+
"""Multiply by 25 as ``100n / 4``."""
|
|
280
|
+
|
|
281
|
+
return (number * 100) // 4
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def multiply_by_5(number: int) -> int:
|
|
285
|
+
"""Multiply by 5 as ``10n / 2``."""
|
|
286
|
+
|
|
287
|
+
return (number * 10) // 2
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def multiply_by_50(number: int) -> int:
|
|
291
|
+
"""Multiply by 50 as ``100n / 2``."""
|
|
292
|
+
|
|
293
|
+
return (number * 100) // 2
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def multiply_by_125(number: int) -> int:
|
|
297
|
+
"""Multiply by 125 as ``1000n / 8``."""
|
|
298
|
+
|
|
299
|
+
return (number * 1000) // 8
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def multiply_by_75(number: int) -> int:
|
|
303
|
+
"""Multiply by 75 as ``3 * 100n / 4``."""
|
|
304
|
+
|
|
305
|
+
return (number * 300) // 4
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def square_near_base(number: int, base: int | None = None) -> int:
|
|
309
|
+
"""Square a number near a power-of-ten base."""
|
|
310
|
+
|
|
311
|
+
return square_near_base_steps(number, base).result
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def square_near_base_steps(number: int, base: int | None = None) -> OperationTrace:
|
|
315
|
+
"""Return a trace for squaring a number near a base."""
|
|
316
|
+
|
|
317
|
+
if base is None:
|
|
318
|
+
base = _nearest_power_of_ten(abs(number))
|
|
319
|
+
if base <= 0:
|
|
320
|
+
raise ValueError("base must be positive")
|
|
321
|
+
x = abs(number)
|
|
322
|
+
diff = x - base
|
|
323
|
+
left = x + diff
|
|
324
|
+
right = diff * diff
|
|
325
|
+
result = (left * base) + right
|
|
326
|
+
return OperationTrace(
|
|
327
|
+
method="Squaring near a base",
|
|
328
|
+
inputs=(number,),
|
|
329
|
+
result=result,
|
|
330
|
+
steps=(
|
|
331
|
+
Step("Choose base", f"base = {base}", base),
|
|
332
|
+
Step("Find deviation", f"{x} - {base}", diff),
|
|
333
|
+
Step("Add deviation", f"{x} + ({diff})", left),
|
|
334
|
+
Step("Square deviation", f"{diff} * {diff}", right),
|
|
335
|
+
Step("Combine", f"({left} * {base}) + {right}", result),
|
|
336
|
+
),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def square_near_50(number: int) -> int:
|
|
341
|
+
"""Square a number near 50 using base 100 and half adjustment."""
|
|
342
|
+
|
|
343
|
+
diff = number - 50
|
|
344
|
+
return ((25 + diff) * 100) + (diff * diff)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def cube_ending_in_5(number: int) -> int:
|
|
348
|
+
"""Return the cube of a number ending in 5."""
|
|
349
|
+
|
|
350
|
+
if abs(number) % 10 != 5:
|
|
351
|
+
raise ValueError("number must end in 5")
|
|
352
|
+
return number**3
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def divide_by_5(number: int) -> float:
|
|
356
|
+
"""Divide by 5 as ``2n / 10``."""
|
|
357
|
+
|
|
358
|
+
return (number * 2) / 10
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def divide_by_25(number: int) -> float:
|
|
362
|
+
"""Divide by 25 as ``4n / 100``."""
|
|
363
|
+
|
|
364
|
+
return (number * 4) / 100
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def fraction_to_percent(numerator: int, denominator: int) -> float:
|
|
368
|
+
"""Convert a fraction to percent."""
|
|
369
|
+
|
|
370
|
+
if denominator == 0:
|
|
371
|
+
raise ZeroDivisionError("denominator cannot be zero")
|
|
372
|
+
return (numerator * 100) / denominator
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def percent_of(percent: float, number: float) -> float:
|
|
376
|
+
"""Find ``percent`` percent of ``number``."""
|
|
377
|
+
|
|
378
|
+
return (percent / 100) * number
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def transpose_percent(percent: float, number: float) -> float:
|
|
382
|
+
"""Use ``a% of b = b% of a``."""
|
|
383
|
+
|
|
384
|
+
return percent_of(number, percent)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def is_divisible_by_3(number: int) -> bool:
|
|
388
|
+
"""Return whether ``number`` is divisible by 3."""
|
|
389
|
+
|
|
390
|
+
return digit_sum(number) % 3 == 0
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def is_divisible_by_9(number: int) -> bool:
|
|
394
|
+
"""Return whether ``number`` is divisible by 9."""
|
|
395
|
+
|
|
396
|
+
return digit_sum(number) % 9 == 0
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def is_divisible_by_11(number: int) -> bool:
|
|
400
|
+
"""Return whether ``number`` is divisible by 11."""
|
|
401
|
+
|
|
402
|
+
digits = [int(char) for char in str(abs(number))]
|
|
403
|
+
alternating = sum(digits[::2]) - sum(digits[1::2])
|
|
404
|
+
return alternating % 11 == 0
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def casting_out_nines_check(a: int, b: int, result: int, operator: str = "*") -> bool:
|
|
408
|
+
"""Check an arithmetic result using the digital-root method.
|
|
409
|
+
|
|
410
|
+
Supported operators are ``"+"``, ``"-"``, and ``"*"``. This is a
|
|
411
|
+
consistency check, not a proof: different wrong answers can share the same
|
|
412
|
+
digital root.
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
if operator == "+":
|
|
416
|
+
expected = digital_root(digital_root(a) + digital_root(b))
|
|
417
|
+
elif operator == "-":
|
|
418
|
+
expected = digital_root(digital_root(a) - digital_root(b))
|
|
419
|
+
elif operator == "*":
|
|
420
|
+
expected = digital_root(digital_root(a) * digital_root(b))
|
|
421
|
+
else:
|
|
422
|
+
raise ValueError("operator must be one of '+', '-', or '*'")
|
|
423
|
+
return expected == digital_root(result)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def nikhilam_multiply(a: int, b: int, base: int | None = None) -> int:
|
|
427
|
+
"""Multiply using the Nikhilam base method and return the result."""
|
|
428
|
+
|
|
429
|
+
return nikhilam_steps(a, b, base).result
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def anurupyena_multiply(
|
|
433
|
+
a: int,
|
|
434
|
+
b: int,
|
|
435
|
+
working_base: int,
|
|
436
|
+
theoretical_base: int | None = None,
|
|
437
|
+
) -> int:
|
|
438
|
+
"""Multiply using a proportionate working base.
|
|
439
|
+
|
|
440
|
+
This follows the Anurupyena idea used when numbers are nearer to a
|
|
441
|
+
convenient multiple or sub-multiple of a power-of-ten base. For example,
|
|
442
|
+
``41 * 41`` can use working base ``50`` and theoretical base ``100``.
|
|
443
|
+
"""
|
|
444
|
+
|
|
445
|
+
return anurupyena_steps(a, b, working_base, theoretical_base).result
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def anurupyena_steps(
|
|
449
|
+
a: int,
|
|
450
|
+
b: int,
|
|
451
|
+
working_base: int,
|
|
452
|
+
theoretical_base: int | None = None,
|
|
453
|
+
) -> OperationTrace:
|
|
454
|
+
"""Return a trace for proportionate-base multiplication."""
|
|
455
|
+
|
|
456
|
+
if working_base <= 0:
|
|
457
|
+
raise ValueError("working_base must be positive")
|
|
458
|
+
if theoretical_base is None:
|
|
459
|
+
theoretical_base = 10 ** len(str(abs(working_base)))
|
|
460
|
+
if theoretical_base <= 0:
|
|
461
|
+
raise ValueError("theoretical_base must be positive")
|
|
462
|
+
if theoretical_base % working_base != 0 and working_base % theoretical_base != 0:
|
|
463
|
+
raise ValueError("bases must have an integer multiple/sub-multiple relationship")
|
|
464
|
+
|
|
465
|
+
sign = _sign(a) * _sign(b)
|
|
466
|
+
x, y = abs(a), abs(b)
|
|
467
|
+
diff_x = x - working_base
|
|
468
|
+
diff_y = y - working_base
|
|
469
|
+
cross = x + diff_y
|
|
470
|
+
right = diff_x * diff_y
|
|
471
|
+
scale_num = working_base
|
|
472
|
+
scale_den = theoretical_base
|
|
473
|
+
left_scaled = (cross * scale_num) // scale_den
|
|
474
|
+
if (cross * scale_num) % scale_den != 0:
|
|
475
|
+
raise ValueError("scaled left side is fractional for these bases")
|
|
476
|
+
result = sign * ((left_scaled * theoretical_base) + right)
|
|
477
|
+
|
|
478
|
+
return OperationTrace(
|
|
479
|
+
method="Anurupyena: proportionately",
|
|
480
|
+
inputs=(a, b),
|
|
481
|
+
result=result,
|
|
482
|
+
steps=(
|
|
483
|
+
Step("Choose bases", f"working {working_base}, theoretical {theoretical_base}", working_base),
|
|
484
|
+
Step("Find deviations", f"{x} - {working_base}, {y} - {working_base}", f"{diff_x}, {diff_y}"),
|
|
485
|
+
Step("Cross add/subtract", f"{x} + ({diff_y})", cross),
|
|
486
|
+
Step("Scale left side", f"{cross} * {working_base} / {theoretical_base}", left_scaled),
|
|
487
|
+
Step("Multiply deviations", f"({diff_x}) * ({diff_y})", right),
|
|
488
|
+
Step("Combine", f"({left_scaled} * {theoretical_base}) + {right}", result),
|
|
489
|
+
),
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def recurring_decimal_unit_fraction(denominator: int, limit: int | None = None) -> str:
|
|
494
|
+
"""Return the recurring decimal expansion of ``1 / denominator``.
|
|
495
|
+
|
|
496
|
+
The helper uses remainder tracking, which mirrors the teaching focus on
|
|
497
|
+
recurring decimal cycles while staying reliable for any integer denominator.
|
|
498
|
+
Repeating parts are wrapped in parentheses, e.g. ``1/19 = 0.(052631...)``.
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
if denominator == 0:
|
|
502
|
+
raise ZeroDivisionError("denominator cannot be zero")
|
|
503
|
+
sign = "-" if denominator < 0 else ""
|
|
504
|
+
denominator = abs(denominator)
|
|
505
|
+
remainder = 1 % denominator
|
|
506
|
+
integer = 1 // denominator
|
|
507
|
+
seen: dict[int, int] = {}
|
|
508
|
+
digits: list[str] = []
|
|
509
|
+
|
|
510
|
+
while remainder and remainder not in seen:
|
|
511
|
+
if limit is not None and len(digits) >= limit:
|
|
512
|
+
return f"{sign}{integer}." + "".join(digits)
|
|
513
|
+
seen[remainder] = len(digits)
|
|
514
|
+
remainder *= 10
|
|
515
|
+
digits.append(str(remainder // denominator))
|
|
516
|
+
remainder %= denominator
|
|
517
|
+
|
|
518
|
+
if remainder == 0:
|
|
519
|
+
return f"{sign}{integer}." + "".join(digits)
|
|
520
|
+
start = seen[remainder]
|
|
521
|
+
non_repeating = "".join(digits[:start])
|
|
522
|
+
repeating = "".join(digits[start:])
|
|
523
|
+
return f"{sign}{integer}.{non_repeating}({repeating})"
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def osculator_for_ending_9(divisor: int) -> int:
|
|
527
|
+
"""Return the Ekadhika-style osculator for divisors ending in 9."""
|
|
528
|
+
|
|
529
|
+
if divisor % 10 != 9:
|
|
530
|
+
raise ValueError("divisor must end in 9")
|
|
531
|
+
return (abs(divisor) // 10) + 1
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def square_root_if_perfect(number: int) -> int | None:
|
|
535
|
+
"""Return the square root when ``number`` is a perfect square."""
|
|
536
|
+
|
|
537
|
+
if number < 0:
|
|
538
|
+
return None
|
|
539
|
+
root = int(number**0.5)
|
|
540
|
+
if root * root == number:
|
|
541
|
+
return root
|
|
542
|
+
if (root + 1) * (root + 1) == number:
|
|
543
|
+
return root + 1
|
|
544
|
+
return None
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def cube_root_if_perfect(number: int) -> int | None:
|
|
548
|
+
"""Return the cube root when ``number`` is a perfect cube."""
|
|
549
|
+
|
|
550
|
+
sign = _sign(number)
|
|
551
|
+
value = abs(number)
|
|
552
|
+
root = round(value ** (1 / 3))
|
|
553
|
+
for candidate in range(max(0, root - 2), root + 3):
|
|
554
|
+
if candidate**3 == value:
|
|
555
|
+
return candidate * sign
|
|
556
|
+
return None
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def nikhilam_steps(a: int, b: int, base: int | None = None) -> OperationTrace:
|
|
560
|
+
"""Work multiplication around a nearby power-of-ten base.
|
|
561
|
+
|
|
562
|
+
Example: ``98 * 97`` uses base ``100``. Differences are ``-2`` and ``-3``;
|
|
563
|
+
left side is ``98 - 3 = 95`` and right side is ``(-2) * (-3) = 6``, padded
|
|
564
|
+
to two digits, giving ``9506``.
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
if base is None:
|
|
568
|
+
highest_digits = max(len(str(abs(a))), len(str(abs(b))))
|
|
569
|
+
base = 10**highest_digits
|
|
570
|
+
if base <= 0:
|
|
571
|
+
raise ValueError("base must be a positive integer")
|
|
572
|
+
|
|
573
|
+
sign = _sign(a) * _sign(b)
|
|
574
|
+
x, y = abs(a), abs(b)
|
|
575
|
+
diff_x = x - base
|
|
576
|
+
diff_y = y - base
|
|
577
|
+
left = x + diff_y
|
|
578
|
+
right = diff_x * diff_y
|
|
579
|
+
result = sign * ((left * base) + right)
|
|
580
|
+
|
|
581
|
+
return OperationTrace(
|
|
582
|
+
method="Nikhilam: all from 9 and the last from 10",
|
|
583
|
+
inputs=(a, b),
|
|
584
|
+
result=result,
|
|
585
|
+
steps=(
|
|
586
|
+
Step("Choose base", f"base = {base}", base),
|
|
587
|
+
Step("Find deviations", f"{x} - {base}, {y} - {base}", f"{diff_x}, {diff_y}"),
|
|
588
|
+
Step("Cross subtract", f"{x} + ({diff_y})", left),
|
|
589
|
+
Step("Multiply deviations", f"({diff_x}) * ({diff_y})", right),
|
|
590
|
+
Step("Combine", f"({left} * {base}) + ({right})", result),
|
|
591
|
+
),
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def urdhva_tiryagbhyam_multiply(a: int, b: int) -> int:
|
|
596
|
+
"""Multiply using the vertically-and-crosswise digit method."""
|
|
597
|
+
|
|
598
|
+
return urdhva_tiryagbhyam_steps(a, b).result
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def urdhva_tiryagbhyam_steps(a: int, b: int) -> OperationTrace:
|
|
602
|
+
"""Return a trace for vertically-and-crosswise multiplication.
|
|
603
|
+
|
|
604
|
+
This implementation handles any integer size by convolving digit columns
|
|
605
|
+
from right to left and carrying forward.
|
|
606
|
+
"""
|
|
607
|
+
|
|
608
|
+
sign = _sign(a) * _sign(b)
|
|
609
|
+
left_digits = _digits_reversed(a)
|
|
610
|
+
right_digits = _digits_reversed(b)
|
|
611
|
+
column_count = len(left_digits) + len(right_digits) - 1
|
|
612
|
+
carry = 0
|
|
613
|
+
output_digits: list[int] = []
|
|
614
|
+
steps: list[Step] = []
|
|
615
|
+
|
|
616
|
+
for column in range(column_count):
|
|
617
|
+
pairs = [
|
|
618
|
+
(i, column - i)
|
|
619
|
+
for i in range(len(left_digits))
|
|
620
|
+
if 0 <= column - i < len(right_digits)
|
|
621
|
+
]
|
|
622
|
+
subtotal = sum(left_digits[i] * right_digits[j] for i, j in pairs)
|
|
623
|
+
total = subtotal + carry
|
|
624
|
+
output_digits.append(total % 10)
|
|
625
|
+
next_carry = total // 10
|
|
626
|
+
expression = " + ".join(
|
|
627
|
+
f"{left_digits[i]}*{right_digits[j]}" for i, j in pairs
|
|
628
|
+
)
|
|
629
|
+
if carry:
|
|
630
|
+
expression = f"{expression} + carry {carry}"
|
|
631
|
+
steps.append(
|
|
632
|
+
Step(
|
|
633
|
+
f"Column {column + 1}",
|
|
634
|
+
expression,
|
|
635
|
+
f"write {total % 10}, carry {next_carry}",
|
|
636
|
+
)
|
|
637
|
+
)
|
|
638
|
+
carry = next_carry
|
|
639
|
+
|
|
640
|
+
while carry:
|
|
641
|
+
output_digits.append(carry % 10)
|
|
642
|
+
steps.append(Step("Carry", str(carry), f"write {carry % 10}"))
|
|
643
|
+
carry //= 10
|
|
644
|
+
|
|
645
|
+
result = int("".join(str(digit) for digit in reversed(output_digits))) * sign
|
|
646
|
+
steps.append(Step("Apply sign", f"sign({a}) * sign({b})", result))
|
|
647
|
+
|
|
648
|
+
return OperationTrace(
|
|
649
|
+
method="Urdhva Tiryagbhyam: vertically and crosswise",
|
|
650
|
+
inputs=(a, b),
|
|
651
|
+
result=result,
|
|
652
|
+
steps=tuple(steps),
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def square_ending_in_5(number: int) -> int:
|
|
657
|
+
"""Square an integer ending in 5 using the Ekadhikena Purvena pattern."""
|
|
658
|
+
|
|
659
|
+
return square_ending_in_5_steps(number).result
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def square_ending_in_5_steps(number: int) -> OperationTrace:
|
|
663
|
+
"""Return a trace for squaring numbers ending in 5.
|
|
664
|
+
|
|
665
|
+
For ``n = 10a + 5``, ``n^2 = a * (a + 1)`` followed by ``25``.
|
|
666
|
+
"""
|
|
667
|
+
|
|
668
|
+
if abs(number) % 10 != 5:
|
|
669
|
+
raise ValueError("number must end in 5")
|
|
670
|
+
|
|
671
|
+
prefix = abs(number) // 10
|
|
672
|
+
left = prefix * (prefix + 1)
|
|
673
|
+
result = (left * 100) + 25
|
|
674
|
+
return OperationTrace(
|
|
675
|
+
method="Ekadhikena Purvena: by one more than the previous one",
|
|
676
|
+
inputs=(number,),
|
|
677
|
+
result=result,
|
|
678
|
+
steps=(
|
|
679
|
+
Step("Separate prefix", f"{abs(number)} = 10*{prefix} + 5", prefix),
|
|
680
|
+
Step("Multiply by next number", f"{prefix} * {prefix + 1}", left),
|
|
681
|
+
Step("Append 25", f"{left} followed by 25", result),
|
|
682
|
+
),
|
|
683
|
+
)
|
vedicmaths/__init__.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vedicmaths
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python helpers for learning and applying Vedic mathematics techniques.
|
|
5
|
+
Author: Karthik Srinivasan
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ksriniv2/vedicmaths
|
|
8
|
+
Project-URL: Issues, https://github.com/ksriniv2/vedicmaths/issues
|
|
9
|
+
Keywords: vedic mathematics,vedic maths,mental math,math,education,arithmetic
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Education
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# Vedic Mathematics
|
|
26
|
+
|
|
27
|
+
Python helpers for learning and applying Vedic mathematics techniques. The
|
|
28
|
+
library focuses on two things:
|
|
29
|
+
|
|
30
|
+
- returning correct arithmetic results as simple Python values
|
|
31
|
+
- exposing step-by-step workings that teachers, students, notebooks, and apps
|
|
32
|
+
can display
|
|
33
|
+
|
|
34
|
+
This project is early-stage and intended to grow into an open-source package of
|
|
35
|
+
well-tested Vedic mathematics methods.
|
|
36
|
+
|
|
37
|
+
The first implementation pass is guided by the parsed source chunks in the
|
|
38
|
+
larger Mr Rishi project, especially
|
|
39
|
+
`data/processed/Vedic_mathematics_chunks.jsonl`.
|
|
40
|
+
|
|
41
|
+
## Install for Development
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
git clone https://github.com/ksriniv2/vedicmaths.git
|
|
45
|
+
cd vedicmaths
|
|
46
|
+
python -m pip install -e ".[dev]"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If you are working from this local starter folder:
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
cd C:\Users\KARTH\OneDrive\Documents\vedicmathematics
|
|
53
|
+
python -m pip install -e ".[dev]"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from vedicmathematics import (
|
|
60
|
+
nikhilam_multiply,
|
|
61
|
+
nikhilam_steps,
|
|
62
|
+
square_ending_in_5,
|
|
63
|
+
urdhva_tiryagbhyam_multiply,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(nikhilam_multiply(98, 97))
|
|
67
|
+
# 9506
|
|
68
|
+
|
|
69
|
+
print(urdhva_tiryagbhyam_multiply(123, 456))
|
|
70
|
+
# 56088
|
|
71
|
+
|
|
72
|
+
print(square_ending_in_5(35))
|
|
73
|
+
# 1225
|
|
74
|
+
|
|
75
|
+
trace = nikhilam_steps(98, 97)
|
|
76
|
+
for step in trace.steps:
|
|
77
|
+
print(step.title, step.expression, "=>", step.value)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
You can also use the shorter package alias:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from vedicmaths import nikhilam_multiply
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Available Functions
|
|
87
|
+
|
|
88
|
+
| Function | Purpose |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `digit_sum(number)` | Sums the decimal digits of a number. |
|
|
91
|
+
| `sum_digits_until_single(number)` | Repeated digit sum, equivalent to digital root. |
|
|
92
|
+
| `nikhilam_multiply(a, b, base=None)` | Multiplies numbers using a nearby base, commonly a power of ten. |
|
|
93
|
+
| `nikhilam_steps(a, b, base=None)` | Returns the worked steps for Nikhilam multiplication. |
|
|
94
|
+
| `anurupyena_multiply(a, b, working_base, theoretical_base=None)` | Proportionate-base multiplication when a multiple or sub-multiple is more convenient. |
|
|
95
|
+
| `anurupyena_steps(a, b, working_base, theoretical_base=None)` | Worked steps for proportionate-base multiplication. |
|
|
96
|
+
| `urdhva_tiryagbhyam_multiply(a, b)` | General multiplication using the vertically-and-crosswise digit method. |
|
|
97
|
+
| `urdhva_tiryagbhyam_steps(a, b)` | Returns the worked steps for vertically-and-crosswise multiplication. |
|
|
98
|
+
| `left_to_right_add(a, b)` | Addition with a trace-friendly column process. |
|
|
99
|
+
| `left_to_right_add_steps(a, b)` | Worked steps for addition. |
|
|
100
|
+
| `subtract_using_complement(a, b)` | Subtraction by complementing the subtrahend from a power of ten. |
|
|
101
|
+
| `subtract_using_complement_steps(a, b)` | Worked steps for complement subtraction. |
|
|
102
|
+
| `complement(number, base=None)` | Complement from a supplied base or next power of ten. |
|
|
103
|
+
| `all_from_9_last_from_10(number)` | Nikhilam-style complement helper. |
|
|
104
|
+
| `vinculum_digits(number)` | Signed digit representation for digits above 5. |
|
|
105
|
+
| `multiply_by_5(number)` | Shortcut for multiplication by 5. |
|
|
106
|
+
| `multiply_by_9(number)` | Shortcut for multiplication by 9. |
|
|
107
|
+
| `multiply_by_11(number)` | Adjacent-sum shortcut for multiplication by 11. |
|
|
108
|
+
| `multiply_by_11_steps(number)` | Worked steps for multiplication by 11. |
|
|
109
|
+
| `multiply_by_12(number)` | Shortcut for multiplication by 12. |
|
|
110
|
+
| `multiply_by_15(number)` | Shortcut for multiplication by 15. |
|
|
111
|
+
| `multiply_by_25(number)` | Shortcut for multiplication by 25. |
|
|
112
|
+
| `multiply_by_50(number)` | Shortcut for multiplication by 50. |
|
|
113
|
+
| `multiply_by_75(number)` | Shortcut for multiplication by 75. |
|
|
114
|
+
| `multiply_by_99(number)` | Shortcut for multiplication by 99. |
|
|
115
|
+
| `multiply_by_125(number)` | Shortcut for multiplication by 125. |
|
|
116
|
+
| `multiply_by_999(number)` | Shortcut for multiplication by 999. |
|
|
117
|
+
| `multiply_by_repeated_9(number, count)` | Multiply by 9, 99, 999, etc. |
|
|
118
|
+
| `square_ending_in_5(number)` | Squares numbers ending in 5 using the "one more than the previous" pattern. |
|
|
119
|
+
| `square_ending_in_5_steps(number)` | Returns the worked steps for squaring a number ending in 5. |
|
|
120
|
+
| `square_near_base(number, base=None)` | Squares numbers close to a power-of-ten base. |
|
|
121
|
+
| `square_near_base_steps(number, base=None)` | Worked steps for near-base squaring. |
|
|
122
|
+
| `square_near_50(number)` | Shortcut for squaring values near 50. |
|
|
123
|
+
| `cube_ending_in_5(number)` | Cubes numbers ending in 5. |
|
|
124
|
+
| `divide_by_5(number)` | Division by 5 as doubling then dividing by 10. |
|
|
125
|
+
| `divide_by_25(number)` | Division by 25 as multiplying by 4 then dividing by 100. |
|
|
126
|
+
| `recurring_decimal_unit_fraction(denominator, limit=None)` | Decimal expansion of `1 / denominator`, with repeating cycles in parentheses. |
|
|
127
|
+
| `osculator_for_ending_9(divisor)` | Ekadhika-style osculator for divisors ending in 9. |
|
|
128
|
+
| `fraction_to_percent(numerator, denominator)` | Converts fractions to percentages. |
|
|
129
|
+
| `percent_of(percent, number)` | Calculates percent of a number. |
|
|
130
|
+
| `transpose_percent(percent, number)` | Uses `a% of b = b% of a`. |
|
|
131
|
+
| `is_divisible_by_3(number)` | Divisibility by 3 using digit sum. |
|
|
132
|
+
| `is_divisible_by_9(number)` | Divisibility by 9 using digit sum. |
|
|
133
|
+
| `is_divisible_by_11(number)` | Divisibility by 11 using alternating digit sums. |
|
|
134
|
+
| `square_root_if_perfect(number)` | Returns the integer square root for perfect squares, otherwise `None`. |
|
|
135
|
+
| `cube_root_if_perfect(number)` | Returns the integer cube root for perfect cubes, otherwise `None`. |
|
|
136
|
+
| `digital_root(number)` | Computes a number's digital root. |
|
|
137
|
+
| `casting_out_nines_check(a, b, result, operator="*")` | Verifies `+`, `-`, or `*` results using digital roots. |
|
|
138
|
+
|
|
139
|
+
## Example Step Trace
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from vedicmathematics import nikhilam_steps
|
|
143
|
+
|
|
144
|
+
trace = nikhilam_steps(98, 97)
|
|
145
|
+
print(trace.result)
|
|
146
|
+
# 9506
|
|
147
|
+
|
|
148
|
+
for step in trace.steps:
|
|
149
|
+
print(f"{step.title}: {step.expression} = {step.value}")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Output:
|
|
153
|
+
|
|
154
|
+
```text
|
|
155
|
+
Choose base: base = 100 = 100
|
|
156
|
+
Find deviations: 98 - 100, 97 - 100 = -2, -3
|
|
157
|
+
Cross subtract: 98 + (-3) = 95
|
|
158
|
+
Multiply deviations: (-2) * (-3) = 6
|
|
159
|
+
Combine: (95 * 100) + (6) = 9506
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Roadmap
|
|
163
|
+
|
|
164
|
+
Good next functions to add:
|
|
165
|
+
|
|
166
|
+
- division using Paravartya Yojayet
|
|
167
|
+
- multiplication by 9, 99, 999, and related bases
|
|
168
|
+
- straight division and auxiliary fractions
|
|
169
|
+
- simple and complex oscillators for divisibility
|
|
170
|
+
- cube-root techniques
|
|
171
|
+
- fraction simplification and recurring decimal patterns
|
|
172
|
+
- richer renderers for traces, such as Markdown and HTML
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
Run tests:
|
|
177
|
+
|
|
178
|
+
```powershell
|
|
179
|
+
python -m pytest
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Project structure:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
vedicmathematics/
|
|
186
|
+
vedicmathematics/
|
|
187
|
+
__init__.py
|
|
188
|
+
arithmetic.py
|
|
189
|
+
tests/
|
|
190
|
+
test_arithmetic.py
|
|
191
|
+
pyproject.toml
|
|
192
|
+
README.md
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Contributing
|
|
196
|
+
|
|
197
|
+
Contributions are welcome. Please include:
|
|
198
|
+
|
|
199
|
+
- a clear function name and docstring
|
|
200
|
+
- tests for regular cases and edge cases
|
|
201
|
+
- a `*_steps` variant when the method is teachable step by step
|
|
202
|
+
- examples in the README when adding a major new method
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
vedicmathematics/__init__.py,sha256=H2lpXSXqIvi-tOYiInAtOa_LBawIh5_m_iQNJ7viuF8,2549
|
|
2
|
+
vedicmathematics/arithmetic.py,sha256=tKHn5YKrIA0wDZSo87uWZinr52AlCStW-1g7IExdf9Q,20459
|
|
3
|
+
vedicmaths/__init__.py,sha256=QWyAfE3LlhVgmXvuRgTq1He327VNwHYuxGvf2pOuS9Q,169
|
|
4
|
+
vedicmaths-0.1.0.dist-info/licenses/LICENSE,sha256=wTEayTs-16iL7xGXCiNPOl_OKJEWfIzuIj1O6_lMmBg,1075
|
|
5
|
+
vedicmaths-0.1.0.dist-info/METADATA,sha256=VrR3xdW4j08RyMFYbepC6m3TBax5pu1zW42Ek6HqwqE,7956
|
|
6
|
+
vedicmaths-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
vedicmaths-0.1.0.dist-info/top_level.txt,sha256=-OO63FTjLwxyoJD2gGjj6EK_oJAgiX__shY1PzV2IEk,28
|
|
8
|
+
vedicmaths-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Karthik Srinivasan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|