Пример #1
0
    def __add__(lhs: IntegerNumber, rhs: IntegerNumber) -> IntegerNumber:
        log(f'Adding {lhs} and {rhs}')
        log_indent()

        def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign:
            if magnitude == WholeNumber.from_int(0):
                return None
            else:
                return default_sign

        if lhs.sign is None:  # sign of None is only when magnitude is 0,  0 + a = a
            sign = rhs.sign
            magnitude = rhs.magnitude
        elif rhs.sign is None:  # sign of None is only when magnitude is 0,  a + 0 = a
            sign = lhs.sign
            magnitude = lhs.magnitude
        elif lhs.sign == rhs.sign:
            magnitude = lhs.magnitude + rhs.magnitude
            sign = determine_sign(magnitude, lhs.sign)
        elif lhs.sign != rhs.sign:
            if rhs.magnitude >= lhs.magnitude:
                magnitude = rhs.magnitude - lhs.magnitude
                sign = determine_sign(magnitude, rhs.sign)
            else:
                magnitude = lhs.magnitude - rhs.magnitude
                sign = determine_sign(magnitude, lhs.sign)

        log_unindent()
        log(f'sign: {sign}, magnitude: {magnitude}')

        return IntegerNumber(sign, magnitude)
Пример #2
0
    def __mul__(lhs: IntegerNumber, rhs: IntegerNumber) -> IntegerNumber:
        log(f'Multiplying {lhs} and {rhs}')
        log_indent()

        def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign:
            if magnitude == WholeNumber.from_int(0):
                return None
            else:
                return default_sign

        if lhs.sign is None:  # when sign isn't set, magnitude is always 0 -- 0 * a = 0
            sign = None
            magnitude = WholeNumber.from_int(0)
        elif rhs.sign is None:  # when sign isn't set, magnitude is always 0 -- a * 0 = 0
            sign = None
            magnitude = WholeNumber.from_int(0)
        elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.POSITIVE) \
                or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.NEGATIVE):
            magnitude = lhs.magnitude * rhs.magnitude
            sign = determine_sign(magnitude, Sign.POSITIVE)
        elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.NEGATIVE) \
                or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.POSITIVE):
            magnitude = lhs.magnitude * rhs.magnitude
            sign = determine_sign(magnitude, Sign.NEGATIVE)

        log_unindent()
        log(f'sign: {sign}, magnitude: {magnitude}')

        return IntegerNumber(sign, magnitude)
Пример #3
0
    def reciprocal(self: FractionNumber) -> FractionNumber:
        # Sign is on the numerator
        log(f'Getting reciprocal of {self}')

        res = FractionNumber(self._denominator, self._numerator)
        log(f'Result: {res}')

        return res
Пример #4
0
    def __truediv__(lhs: FractionNumber,
                    rhs: FractionNumber) -> FractionNumber:
        # Sign is only kept on the numerator, not the denominator
        log(f'Dividing {lhs} and {rhs}')

        res = FractionNumber(lhs._numerator * rhs._denominator,
                             lhs._denominator * rhs._numerator)
        log(f'Result: {res}')

        return res
Пример #5
0
    def to_words(self: IntegerNumber) -> str:
        log(f'Converting {self}...')

        output = ''
        if self.sign == Sign.NEGATIVE:
            output += 'negative '
        output += self.magnitude.to_words()

        log_unindent()
        log(f'{output}')

        return output.lstrip()
Пример #6
0
    def __eq__(lhs: FractionalNumber, rhs: FractionalNumber) -> bool:
        if not isinstance(rhs, FractionalNumber):
            raise Exception()

        log(f'Equality testing {lhs} and {rhs}...')
        log_indent()

        ret = lhs.digits == rhs.digits

        log_unindent()
        log(f'{ret}')

        return ret
Пример #7
0
    def to_words(self: FractionNumber) -> str:
        log(f'Converting {self}...')

        output = ''
        if self.sign == Sign.NEGATIVE:
            output += 'negative '
        output += self.numerator.to_words()
        output += ' over '
        output += self.denominator.to_words()

        log_unindent()
        log(f'{output}')

        return output.lstrip()
Пример #8
0
    def __add__(lhs: FractionNumber, rhs: FractionNumber) -> FractionNumber:
        # Sign is only kept on the numerator, not the denominator
        log(f'Adding {lhs} and {rhs}...')
        log_indent()

        log(f'Converting {lhs} and {rhs} to equivalent fractions with least common denominator...'
            )
        lhs, rhs = FractionNumber.common_denominator_lcm(lhs, rhs)
        log(f'Equivalent fractions: {lhs} and {rhs}')
        log(f'Adding numerators of {lhs} and {rhs}...')
        res = FractionNumber(lhs._numerator + rhs._numerator, lhs._denominator)

        log_unindent()
        log(f'Result: {res}')

        return res
Пример #9
0
def factor_fast(num: WholeNumber) -> Set[WholeNumber]:
    log(f'Factoring {num}...')
    log_indent()

    factors: Set[WholeNumber] = set()
    for factor1 in WholeNumber.range(WholeNumber.from_int(1),
                                     num,
                                     end_inclusive=True):
        log(f'Test if {factor1} is a factor...')
        factor2, remainder = num / factor1
        if remainder == WholeNumber.from_int(0):
            factors.add(factor1)
            factors.add(factor2)
            log(f'Yes: ({factor1} and {factor2} are factors)')
        else:
            log(f'No')

    log_unindent()
    log(f'{factors}')

    return factors
Пример #10
0
    def choose_start_modifier_for_divte(
            start_test_num: DecimalNumber) -> DecimalNumber:
        log(f'Choosing a starting modifier for {start_test_num}...')
        log_indent()

        log(f'{start_test_num} has {len(start_test_num.whole.digits)} digits')
        num_of_zeros = len(start_test_num.whole.digits) - 1
        start_modifier_num = DecimalNumber.from_str('1' + '0' * num_of_zeros)

        log(f'Starting modifier: {start_modifier_num}')

        log_unindent()
        log(f'{start_modifier_num}')
        return start_modifier_num
Пример #11
0
    def __truediv__(lhs: IntegerNumber, rhs: IntegerNumber) -> (IntegerNumber, IntegerNumber):
        log(f'Dividing {lhs} and {rhs}')
        log_indent()

        def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign:
            if magnitude == WholeNumber.from_int(0):
                return None
            else:
                return default_sign

        if lhs.sign is None:  # when sign isn't set, magnitude is always 0 -- 0 / a = 0
            (quotient_magnitude, remainder_magnitude) = lhs.magnitude / rhs.magnitude
            quotient_sign = None
            remainder_sign = None
        elif rhs.sign is None:  # when sign isn't set, magnitude is always 0 -- a / 0 = err
            raise Exception('Cannot divide by 0')
        elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.POSITIVE) \
                or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.NEGATIVE):
            (quotient_magnitude, remainder_magnitude) = lhs.magnitude / rhs.magnitude
            quotient_sign = determine_sign(quotient_magnitude, Sign.POSITIVE)
            remainder_sign = determine_sign(remainder_magnitude, Sign.POSITIVE)
        elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.NEGATIVE) \
                or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.POSITIVE):
            (quotient_magnitude, remainder_magnitude) = lhs.magnitude / rhs.magnitude
            quotient_sign = determine_sign(quotient_magnitude, Sign.NEGATIVE)
            remainder_sign = determine_sign(remainder_magnitude, Sign.NEGATIVE)

        log_unindent()
        log(f'QUOTIENT: sign: {quotient_sign}, magnitude: {quotient_magnitude}')
        log(f'REMAINDER: sign: {remainder_sign}, magnitude: {remainder_magnitude}')

        return IntegerNumber(quotient_sign, quotient_magnitude), IntegerNumber(remainder_sign, remainder_magnitude)
Пример #12
0
def factor_naive(num: WholeNumber) -> Set[WholeNumber]:
    log(f'Factoring {num}...')
    log_indent()

    factors: Set[WholeNumber] = set()
    for factor1 in WholeNumber.range(WholeNumber.from_int(1),
                                     num,
                                     end_inclusive=True):
        for factor2 in WholeNumber.range(WholeNumber.from_int(1),
                                         num,
                                         end_inclusive=True):
            log(f'Testing if {factor1} and {factor2} are factors...')
            if factor1 * factor2 == num:
                factors.add(factor1)
                factors.add(factor2)
                log(f'Yes')
            else:
                log(f'No')

    log_unindent()
    log(f'{factors}')

    return factors
Пример #13
0
def gcd_euclid(num1: WholeNumber, num2: WholeNumber) -> WholeNumber:
    log(f'Calculating gcd for {num1} and {num2}...')
    log_indent()

    next_nums = [num1, num2]

    while True:
        log(f'Sorting {next_nums}...')
        next_nums.sort()  # sort smallest to largest
        next_nums.reverse()  # reverse it so that it's largest to largest
        log(f'Checking if finished ({next_nums[1]} == 0?)...')
        if next_nums[1] == WholeNumber.from_int(0):
            found = next_nums[0]
            break

        log(f'Dividing {next_nums} and grabbing the remainder for the next test...'
            )
        _, remainder = next_nums[0] / next_nums[1]
        next_nums = [next_nums[1], remainder]

    log_unindent()
    log(f'GCD is {found}')
    return found
Пример #14
0
    def __sub__(lhs: IntegerNumber, rhs: IntegerNumber) -> IntegerNumber:
        log(f'Subtracting {lhs} and {rhs}')
        log_indent()

        def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign:
            if magnitude == WholeNumber.from_int(0):
                return None
            else:
                return default_sign

        def flip_sign(sign: Sign) -> Sign:
            if sign == Sign.POSITIVE:
                return Sign.NEGATIVE
            elif sign == Sign.NEGATIVE:
                return Sign.POSITIVE

        if lhs.sign is None:  # sign of None is only when magnitude is 0,  0 - a = -a
            sign = flip_sign(rhs.sign)
            magnitude = rhs.magnitude
        elif rhs.sign is None:  # sign of None is only when magnitude is 0,  a - 0 = a
            sign = lhs.sign
            magnitude = lhs.magnitude
        elif lhs.sign == rhs.sign:
            if rhs.magnitude >= lhs.magnitude:
                magnitude = rhs.magnitude - lhs.magnitude
                sign = determine_sign(magnitude, flip_sign(lhs.sign))
            else:
                magnitude = lhs.magnitude - rhs.magnitude
                sign = determine_sign(magnitude, lhs.sign)
        elif lhs.sign != rhs.sign:
            magnitude = lhs.magnitude + rhs.magnitude
            sign = determine_sign(magnitude, lhs.sign)

        log_unindent()
        log(f'sign: {sign}, magnitude: {magnitude}')

        return IntegerNumber(sign, magnitude)
Пример #15
0
    def __mul__(lhs: FractionNumber, rhs: FractionNumber) -> FractionNumber:
        # Sign is only kept on the numerator, not the denominator
        log(f'Multiplying {lhs} and {rhs}')
        log_indent()

        log(f'Multiplying numerators {lhs._numerator} and {rhs._numerator}...')
        numerator = lhs._numerator * rhs._numerator

        log(f'Multiplying denominators {lhs._denominator} and {rhs._denominator}...'
            )
        denominator = lhs._denominator * rhs._denominator

        res = FractionNumber(numerator, denominator)

        log_unindent()
        log(f'Result: {res}')

        return res
Пример #16
0
def is_prime(num: WholeNumber) -> bool:
    log(f'Test if {num} is prime...')
    log_indent()

    num_factors = factor_fastest(num)

    # At a minimum, all counting numbers have the factors 1 and the number itself (2 factors). If
    # there are more factore than that, it's a composite. Otherwise, it's a primse.

    log_unindent()
    if len(num_factors) == 2:
        log(f'{num}\'s factors are {num_factors} -- it is a prime')
        return True
    else:
        log(f'{num}\'s factors are {num_factors} -- it is a composite')
        return False
Пример #17
0
def factor_tree(num: WholeNumber) -> FactorTreeNode:
    log(f'Creating factor tree for {num}...')

    factors = factor_fastest(num)

    # remove factor pairs that can't used in factor true: (1, num) or (num, 1)
    factors = set(
        [f for f in factors if f != WholeNumber.from_int(1) and f != num])

    ret = FactorTreeNode()
    if len(factors) == 0:
        ret.value = num
        log(f'Cannot factor {num} is prime -- resulting tree: {ret}')
    else:
        factor1 = next(iter(factors))
        factor2, _ = num / factor1
        ret.value = num
        ret.left = factor_tree(factor1)
        ret.right = factor_tree(factor2)
        log(f'Factored {num} to {factor1} and {factor2} -- resulting tree: {ret}'
            )
    return ret
Пример #18
0
    def round(self: DecimalNumber, position: str) -> DecimalNumber:
        log(f'Rounding {self} at {position} position...')
        log_indent()

        position = position.strip()
        if position.endswith('s'):
            position = position[:-1]
        position_word_to_index = {
            'hundred-quintillion': 20,
            'ten-quintillion': 19,
            'quintillion': 18,
            'hundred-quadillion': 17,
            'ten-quadrillion': 16,
            'quadrillion': 15,
            'hundred-trillion': 14,
            'ten-trillion': 13,
            'trillion': 12,
            'hundred-billion': 11,
            'ten-billion': 10,
            'billion': 9,
            'hundred-million': 8,
            'ten-million': 7,
            'million': 6,
            'hundred-thousand': 5,
            'ten-thousand': 4,
            'thousand': 3,
            'hundred': 2,
            'ten': 1,
            'one': 0,
            'tenth': -1,
            'hundredth': -2,
            'thousandth': -3,
            'ten-thousandth': -4,
            'hundred-thousandth': -5,
            'millionth': -6,
            'ten-millionth': -7,
            'hundred-millionth': -8,
            'billionth': -9,
            'ten-billionth': -10,
            'hundred-billionth': -11,
            'trillionth': -12,
            'ten-trillionth': -13,
            'hundred-trillionth': -14,
            'quadrillionth': -15,
            'ten-quadrillionth': -16,
            'hundred-quadillionth': -17,
            'quintillionth': -18,
            'ten-quintillionth': -19,
            'hundred-quintillionth': -20,
        }
        position_idx = position_word_to_index[position]
        if position_idx is None:
            raise Exception('Position unknown')

        next_position_idx = position_idx - 1

        log(f'Determining adder based on following position...')
        log_indent()
        log(f'Checking if digit at following position is >= 5...')
        following_digit = WholeNumber.from_digit(self[next_position_idx])
        if following_digit >= WholeNumber.from_str("5"):
            log(f'True ({following_digit} >= 5), deriving adder based on position...'
                )
            if position_idx >= 0:
                adder = DecimalNumber(
                    self.sign, WholeNumber.from_str('1' + '0' * position_idx),
                    FractionalNumber.from_str('0'))
            else:
                adder = DecimalNumber(
                    self.sign, WholeNumber.from_str('0'),
                    FractionalNumber.from_str('0' * -(position_idx + 1) + '1'))
        else:
            log(f'False ({following_digit} < 5), setting adder to 0...')
            adder = DecimalNumber.from_str('0')
        log_unindent()
        log(f'{adder}')

        log(f'Adding {adder} to {self}...')
        ret = self.copy() + adder
        log(f'{ret}')

        log(f'Truncating all following positions...')
        log_indent()
        if position_idx >= 0:
            for i in range(0, position_idx):
                ret[i] = Digit(0)
                log(f'{ret}')
            for i in range(0, len(self.fractional.digits)):
                ret[-i - 1] = Digit(0)
                log(f'{ret}')
        else:
            for i in range(-position_idx, len(self.fractional.digits)):
                ret[-i - 1] = Digit(0)
                log(f'{ret}')
        log_unindent()
        log(f'{ret}')

        log_unindent()
        log(f'{ret}')

        return ret
Пример #19
0
def ladder(num: WholeNumber) -> Set[WholeNumber]:
    prime_factors: List[WholeNumber] = []

    log(f'Testing primes (using ladder method) to see which is factor of {num}...'
        )

    log_indent()
    while not is_prime(num):
        prime_to_test = WholeNumber.from_int(2)

        while True:
            log(f'Testing if {prime_to_test} is divisible by {num}...')
            (new_num, remainder) = num / prime_to_test
            if remainder == WholeNumber.from_int(0):
                break
            prime_to_test = calculate_next_prime(prime_to_test)

        log(f'Found! {prime_to_test} is a prime factor -- {new_num} * {prime_to_test} = {num}'
            )
        prime_factors.append(prime_to_test)
        num = new_num

        log(f'Testing primes to see which is factor of {num}...')

    log(f'{num} itself is a prime!')
    prime_factors.append(num)

    log_unindent()
    log(f'Prime factors: {prime_factors}')

    return prime_factors
Пример #20
0
    def trial_and_error_div(dividend: DecimalNumber,
                            divisor: DecimalNumber) -> DecimalNumber:
        log(f'Dividing {dividend} and {divisor}...')
        log_indent()

        log(f'Ensuring {dividend} / {divisor} results in a terminating decimal...'
            )
        if not DecimalNumber.will_division_terminate(dividend, divisor):
            raise Exception('Resulting decimal will be non-terminating')

        log(f'Treating {dividend} and {divisor} as non-negative to perform the algorithm...'
            )
        orig_dividend_sign = dividend.sign
        orig_divisor_sign = divisor.sign
        if dividend.sign == Sign.NEGATIVE:
            dividend *= DecimalNumber.from_str('-1.0')
        if divisor.sign == Sign.NEGATIVE:
            divisor *= DecimalNumber.from_str('-1.0')
        log(f'Non-negative: {dividend} and {divisor}...')

        log(f'Calculating starting test number...')
        test = DecimalNumber.choose_start_test_num_for_divte(divisor, dividend)
        log(f'{test}')

        log(f'Calculating starting modifier for test number...')
        modifier = DecimalNumber.choose_start_modifier_for_divte(test)
        log(f'{modifier}')

        while True:
            log(f'Testing {test}: {test} * {divisor}...')
            test_res = test * divisor
            log(f'{test_res}')

            log(f'Is {test_res} ==, >, or < to {dividend}? ...')
            log_indent()
            try:
                if test_res == dividend:
                    log(f'{test_res} == {dividend} -- Found')
                    break
                elif test_res > dividend:
                    log(f'{test_res} > {dividend} -- Decrementing {test} by {modifier} until not >...'
                        )
                    log_indent()
                    while True:
                        log(f'Decrementing {test} by {modifier}...')
                        test -= modifier
                        log(f'{test} * {divisor}...')
                        modify_res = test * divisor
                        log(f'{modify_res}')
                        if not modify_res > dividend:
                            break
                    log_unindent()
                    log(f'Done: {test}')
                elif test_res < dividend:
                    log(f'{test_res} < {dividend} -- Incrementing {test} by {modifier} until not <...'
                        )
                    log_indent()
                    while True:
                        log(f'Incrementing {test} by {modifier}...')
                        test += modifier
                        log(f'{test} * {divisor}...')
                        modify_res = test * divisor
                        log(f'{modify_res}')
                        if not modify_res < dividend:
                            break
                    log_unindent()
                    log(f'Done: {test}')
            finally:
                log_unindent()

            log(f'Calculating position for next test...')
            modifier *= DecimalNumber.from_str('0.1')
            log(f'{modifier}')

        log_unindent()
        log(f'{test}')

        log(f'Modifying sign of {test} based on original sign of dividend ({orig_dividend_sign}) and divisor ({orig_divisor_sign})...'
            )
        if orig_dividend_sign == Sign.NEGATIVE and orig_divisor_sign != Sign.NEGATIVE \
                or orig_dividend_sign != Sign.NEGATIVE and orig_divisor_sign == Sign.NEGATIVE:
            test *= DecimalNumber.from_str('-1.0')
        log(f'{test}')

        return test
Пример #21
0
    def choose_start_test_num_for_divte(
            input1: DecimalNumber,
            expected_product: DecimalNumber) -> DecimalNumber:
        log(f'Choosing a starting number to find {input1} \\* ? = {expected_product}...'
            )
        log_indent()

        log(f'Checking which case should apply...')
        if input1 < DecimalNumber.from_str(
                '1.0') and expected_product >= DecimalNumber.from_str('1.0'):
            log(f'{input1} has {len(input1.fractional.digits)} fractional digits'
                )
            log(f'{expected_product}\'s has {len(expected_product.whole.digits)} whole digits'
                )
            num_of_zeros = len(expected_product.whole.digits) + len(
                input1.fractional.digits) - 1
            start_test_num = DecimalNumber.from_str('1' + '0' * num_of_zeros)
        else:
            log(f'{input1} has {len(input1.whole.digits)} whole digits')
            log(f'{expected_product}\'s has {len(expected_product.whole.digits)} whole digits'
                )
            num_of_zeros = len(expected_product.whole.digits) - len(
                input1.whole.digits)
            start_test_num = DecimalNumber.from_str('1' + '0' * num_of_zeros)

        log(f'Starting number: {start_test_num}')

        log_unindent()
        log(f'{start_test_num}')
        return start_test_num
Пример #22
0
    def will_division_terminate(lhs: DecimalNumber,
                                rhs: DecimalNumber) -> bool:
        log(f'Checking if {lhs} / {rhs} results in a non-terminating decimal...'
            )
        log_indent()

        adjust_len_self = len(lhs.fractional.digits)
        adjust_len_other = len(rhs.fractional.digits)

        log(f'Generating mock integer number for {lhs}...')
        lhs_extra_0s = adjust_len_self - len(lhs.fractional.digits)
        lhs_combined_digits = lhs.fractional.digits + lhs.whole.digits
        lhs_combined_digits[0:0] = [Digit(0)] * lhs_extra_0s
        mock_self = IntegerNumber(lhs.sign, WholeNumber(lhs_combined_digits))
        log(f'{mock_self}')

        log(f'Generating mock integer number for {rhs}...')
        rhs_extra_0s = adjust_len_other - len(rhs.fractional.digits)
        rhs_combined_digits = rhs.fractional.digits + rhs.whole.digits
        rhs_combined_digits[0:0] = [Digit(0)] * rhs_extra_0s
        mock_other = IntegerNumber(rhs.sign, WholeNumber(rhs_combined_digits))
        log(f'{mock_other}')

        log(f'Generating mock fraction for {lhs} / {rhs}...')
        mock_fraction = FractionNumber(mock_self, mock_other)
        log(f'{mock_fraction}')

        log(f'Simplifying mock fraction...')
        mock_fraction = mock_fraction.simplify()
        log(f'{mock_fraction}')

        log(f'Checking if prime factors of denom ({mock_fraction.denominator}) is {{}}, {{2}}, {{5}}, or {{2,5}}...'
            )
        mock_fraction_denom_prime_factors = set(
            factor_tree(mock_fraction.denominator).get_prime_factors())
        if not (
            {WholeNumber.from_str('2'),
             WholeNumber.from_str('5')} == mock_fraction_denom_prime_factors or
            {WholeNumber.from_str('2')} == mock_fraction_denom_prime_factors or
            {WholeNumber.from_str('5')} == mock_fraction_denom_prime_factors
                or 0 == len(mock_fraction_denom_prime_factors)):
            ret = False
            log(f'{ret} -- Won\'t terminate.')
        else:
            ret = True
            log(f'{ret} -- Will terminate.')

        log_unindent()
        log(f'{ret}')
        return ret
Пример #23
0
    def __mul__(lhs: DecimalNumber, rhs: DecimalNumber) -> DecimalNumber:
        log(f'Multiplying {lhs} and {rhs}...')
        log_indent()

        adjust_len_self = len(lhs.fractional.digits)
        adjust_len_other = len(rhs.fractional.digits)

        log(f'Generating mock integer number for {lhs}...')
        lhs_extra_0s = adjust_len_self - len(lhs.fractional.digits)
        lhs_combined_digits = lhs.fractional.digits + lhs.whole.digits
        lhs_combined_digits[0:0] = [Digit(0)] * lhs_extra_0s
        mock_self = IntegerNumber(lhs.sign, WholeNumber(lhs_combined_digits))
        log(f'{mock_self}')

        log(f'Generating mock integer number for {rhs}...')
        rhs_extra_0s = adjust_len_other - len(rhs.fractional.digits)
        rhs_combined_digits = rhs.fractional.digits + rhs.whole.digits
        rhs_combined_digits[0:0] = [Digit(0)] * rhs_extra_0s
        mock_other = IntegerNumber(rhs.sign, WholeNumber(rhs_combined_digits))
        log(f'{mock_other}')

        log(f'Performing {mock_self} * {mock_other}...')
        mock_ret = mock_self * mock_other
        log(f'{mock_ret}')

        log(f'Unmocking {mock_ret} back to decimal...')
        unadjust_len = adjust_len_self + adjust_len_other
        ret_sign = mock_ret.sign
        ret_fractional_digits = [
            mock_ret.magnitude[i] for i in range(0, unadjust_len)
        ]
        ret_whole_digits = [
            mock_ret.magnitude[i]
            for i in range(unadjust_len, len(mock_ret.magnitude.digits))
        ]
        ret = DecimalNumber(ret_sign, WholeNumber(ret_whole_digits),
                            FractionalNumber(ret_fractional_digits))
        log(f'{ret}')

        log_unindent()
        log(f'{ret}')

        return ret
Пример #24
0
    def from_fraction(value: FractionNumber) -> DecimalNumber:
        log(f'Converting {value} to a decimal number...')
        log_indent()

        log(f'Converting {value} to suitable fraction...')
        value = DecimalNumber.to_suitable_fraction(value)
        log(f'{value}')

        num = value.numerator
        denom = value.denominator
        if not str(denom).startswith('1'):
            raise Exception('Denominator must be power of 10')
        elif not set(str(denom)[1:]) == set('0') and not set(
                str(denom)[1:]) == set():
            raise Exception('Denominator must be power of 10')

        log(f'Resolving fraction {value}...')
        whole, remaining = num / denom
        log(f'{whole} wholes and {remaining} remaining')

        log(f'Converting {remaining} of {denom} to fractional value...')
        num_digits_in_rem = len(remaining.digits)
        num_0s_in_den = len(
            denom.digits
        ) - 1  # starts with 1 followed by 0s, so - 1 to ignore the starting 1
        num_0s_to_prepend_to_rem = num_0s_in_den - num_digits_in_rem
        fractional_digits = remaining.digits[:]  # copy digits
        fractional_digits = fractional_digits + [Digit(
            0)] * num_0s_to_prepend_to_rem  # this prepending 0s...
        # you might be confused because the
        # 0s are being addeed to the end, but
        # that's how FractionalNumber expects
        # digits -- in reversed order
        fractional = FractionalNumber(fractional_digits)
        log(f'{fractional}')

        sign = value.sign

        log_unindent()

        ret = DecimalNumber(sign, whole, fractional)
        log(f'Decimal number: {ret}')
        return ret
Пример #25
0
    def as_fraction(self: DecimalNumber) -> FractionNumber:
        log(f'Converting {self} to fraction number...')
        log_indent()

        log(f'Determining denominator based on length of fractional portion ({self.fractional})...'
            )
        denom = IntegerNumber.from_str('1' + '0' * len(self.fractional.digits))
        log(f'{denom}')

        log(f'Converting fractional portion ({self.fractional} to fraction...')
        fractional_fraction = FractionNumber(
            IntegerNumber.from_str(str(self.fractional)), denom)
        log(f'{fractional_fraction}')

        log(f'Converting whole portion ({self.whole}) to fraction...')
        whole_fraction = FractionNumber.from_whole(self.whole)
        log(f'{whole_fraction}')

        log(f'Adding ({whole_fraction}) to ({fractional_fraction})...')
        fraction = whole_fraction + fractional_fraction
        log(f'{fraction}')

        log(f'Applying sign of ({self.sign}) to {fraction}...')
        if self.sign == Sign.NEGATIVE:
            fraction = fraction * FractionNumber.from_str(
                "-1/1")  # make sign negative
        log(f'{fraction}')

        log_unindent()

        return fraction
Пример #26
0
    def to_words(self: DecimalNumber) -> str:
        fractional_len_to_suffixes = {
            1: 'tenth',
            2: 'hundredth',
            3: 'thousandth',
            4: 'ten-thousandth',
            5: 'hundred-thousandth',
            6: 'millionth',
            7: 'ten-millionth',
            8: 'hundred-millionth',
            9: 'billionth',
            10: 'ten-billionth',
            11: 'hundred-billionth',
            12: 'trillionth',
            13: 'ten-trillionth',
            14: 'hundred-trillionth',
            15: 'quadrillionth',
            16: 'ten-quadrillionth',
            17: 'hundred-quadillionth',
            18: 'quintillionth',
            19: 'ten-quintillionth',
            20: 'hundred-quintillionth',
        }

        log(f'Converting {self}...')
        log_indent()

        log(f'Converting whole portion to words...')
        whole_words = self.whole.to_words()
        log(f'Whole as words: {whole_words}')

        log(f'Converting fractional portion to words...')
        fractional_words = WholeNumber.from_str(str(
            self.fractional)).to_words()
        log(f'fractional as words: {fractional_words}')

        output = ''
        if self.whole == WholeNumber.from_str(
                '0') and self.fractional == FractionalNumber.from_str('0'):
            output += 'zero'
        else:
            if self.sign == Sign.NEGATIVE:
                output += 'negative '

            if self.whole != WholeNumber.from_str('0'):
                output += whole_words

            if self.whole != WholeNumber.from_str(
                    '0') and self.fractional != FractionalNumber.from_str('0'):
                output += ' and '

            if self.fractional != FractionalNumber.from_str('0'):
                output += fractional_words
                suffix = fractional_len_to_suffixes[len(
                    self.fractional.digits)]
                if suffix is None:
                    raise Exception('Fractional too large')
                log(f'Fractional suffix: {suffix}')
                if self.fractional != FractionalNumber.from_str(
                        '0'):  # pluralize suffix if more than 1
                    suffix += 's'
                output += ' ' + suffix

        log_unindent()
        log(f'{output}')

        return output.strip()
Пример #27
0
def gcd_factor(num1: WholeNumber, num2: WholeNumber) -> WholeNumber:
    log(f'Calculating gcd for {num1} and {num2}...')
    log_indent()

    log(f'Calculating factors for {num1}...')
    factors1 = factor_fastest(num1)
    log(f'Factors for {num1}: {factors1}')

    log(f'Calculating factors for {num2}...')
    factors2 = factor_fastest(num2)
    log(f'Factors for {num2}: {factors2}')

    log(f'Finding common factors...')
    common_factors = factors1 & factors2  # set intersection
    log(f'Common factors for {num1} and {num2}: {common_factors}')

    found = max(common_factors)

    log_unindent()
    log(f'GCD is {found}')
    return found
Пример #28
0
    def __eq__(lhs: DecimalNumber, rhs: DecimalNumber) -> bool:
        class FailedTestException(Exception):
            pass

        log(f'Equality testing {lhs} and {rhs}...')
        log_indent()

        try:
            log(f'Testing sign...')
            sign_eq = lhs.sign == rhs.sign
            if not sign_eq:
                raise FailedTestException()
            log(f'Equal')

            log(f'Testing whole...')
            whole_eq = lhs.whole == rhs.whole
            if not whole_eq:
                raise FailedTestException()
            log(f'Equal')

            log(f'Testing fractional...')
            fractional_eq = lhs.fractional == rhs.fractional
            if not fractional_eq:
                raise FailedTestException()
            log(f'Equal')

            ret = True
        except FailedTestException:
            log(f'Not equal')
            ret = False

        log_unindent()
        log(f'{ret}')

        return ret
Пример #29
0
def gcd_naive(num1: WholeNumber, num2: WholeNumber) -> WholeNumber:
    log(f'Calculating gcd for {num1} and {num2}...')
    log_indent()

    log(f'Sorting to determine smaller input...')
    min_num = min(num1, num2)

    log(f'Testing up to smaller input ({min_num})...')
    log_indent()
    for i in WholeNumber.range(WholeNumber.from_str('1'), min_num, True):
        log(f'Testing {i}...')
        quotient1, remainder1 = num1 / i
        quotient2, remainder2 = num2 / i
        if remainder1 == 0 and remainder2 == 0:
            log(f'{num1} and {num2} are both divisible by {i}...')
            found = i
        else:
            log(f'{num1} and {num2} are NOT both divisible by {i}...')
    log_unindent()

    log_unindent()
    log(f'GCD is {found}')
    return found
Пример #30
0
    def __gt__(lhs: DecimalNumber, rhs: DecimalNumber) -> bool:
        class PassedTestException(Exception):
            pass

        class FailedTestException(Exception):
            pass

        log(f'Greater than testing {lhs} and {rhs}...')
        log_indent()

        try:
            log(f'Testing sign...')
            sign_gt = lhs.sign == Sign.POSITIVE and (rhs.sign == Sign.NEGATIVE
                                                     or rhs.sign is None)
            if sign_gt:
                raise PassedTestException()
            sign_eq = lhs.sign == rhs.sign
            if not sign_eq:
                raise FailedTestException()
            log(f'Equal')

            log(f'Testing whole...')
            if lhs.sign != Sign.NEGATIVE:
                whole_gt = lhs.whole > rhs.whole
            else:
                whole_gt = lhs.whole < rhs.whole
            if whole_gt:
                raise PassedTestException()
            whole_eq = lhs.whole == rhs.whole
            if not whole_eq:
                raise FailedTestException()
            log(f'Equal')

            log(f'Testing fractional...')
            if lhs.sign != Sign.NEGATIVE:
                fractional_gt = lhs.fractional > rhs.fractional
            else:
                fractional_gt = lhs.fractional < rhs.fractional
            if fractional_gt:
                raise PassedTestException()
            fractional_eq = lhs.fractional == rhs.fractional
            if not fractional_eq:
                raise FailedTestException()
            log(f'Equal')

            ret = False
        except PassedTestException:
            log(f'Greater')
            ret = True
        except FailedTestException:
            log(f'Not greater or equal')
            ret = False

        log_unindent()
        log(f'{ret}')

        return ret