snake_case__				= range(2, 20 + 1)
snake_case__				= [10**k for k in range(ks[-1] + 1)]
snake_case__				= {}
def       __magic_name__(  __UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)						->    Tuple:



      '''simple docstring'''

      _lowerCamelCase			    =				sum(a_i[j] for j in range(__UpperCAmelCase ,						len(__UpperCAmelCase		)		)		)
      _lowerCamelCase			    =				sum(a_i[j] * base[j] for j in range(min(len(__UpperCAmelCase		) ,						__UpperCAmelCase		)		)		)

      _lowerCamelCase    ,					_lowerCamelCase			    =				0, 0
      _lowerCamelCase			    =				n - i

      _lowerCamelCase			    =				memo.get(__UpperCAmelCase		)

      if sub_memo is not None:
            _lowerCamelCase			    =				sub_memo.get(__UpperCAmelCase		)

            if jumps is not None and len(__UpperCAmelCase		) > 0:
                  # find and make the largest jump without going over
                  _lowerCamelCase			    =				-1
                  for _k in range(len(__UpperCAmelCase		) - 1 ,						-1 ,						-1		):
                        if jumps[_k][2] <= k and jumps[_k][1] <= max_dn:
                              _lowerCamelCase			    =				_k
                              break

                  if max_jump >= 0:
                        _lowerCamelCase    ,					_lowerCamelCase    ,					_lowerCamelCase			    =				jumps[max_jump]
                        # since the difference between jumps is cached, add c
                        _lowerCamelCase			    =				diff + c
                        for j in range(min(__UpperCAmelCase ,						len(__UpperCAmelCase		)		)		):
                              _lowerCamelCase    ,					_lowerCamelCase			    =				divmod(__UpperCAmelCase ,						10		)
                        if new_c > 0:
                              add(__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)

            else:
                  _lowerCamelCase			    =				[]
      else:
            _lowerCamelCase			    =				{c: []}
            _lowerCamelCase			    =				sub_memo

      if dn >= max_dn or c + diff >= base[k]:
            return diff, dn

      if k > ks[0]:
            while True:
                  # keep doing smaller jumps
                  _lowerCamelCase    ,					_lowerCamelCase			    =				next_term(__UpperCAmelCase ,						k - 1 ,						i + dn ,						__UpperCAmelCase		)
                  diff += _diff
                  dn += terms_jumped

                  if dn >= max_dn or c + diff >= base[k]:
                        break
      else:
            # would be too small a jump, just compute sequential terms instead
            _lowerCamelCase    ,					_lowerCamelCase			    =				compute(__UpperCAmelCase ,						__UpperCAmelCase ,						i + dn ,						__UpperCAmelCase		)
            diff += _diff
            dn += terms_jumped

      _lowerCamelCase			    =				sub_memo[c]

      # keep jumps sorted by # of terms skipped
      _lowerCamelCase			    =				0
      while j < len(__UpperCAmelCase		):
            if jumps[j][1] > dn:
                  break
            j += 1

      # cache the jump for this value digitsum(b) and c
      sub_memo[c].insert(__UpperCAmelCase ,						(diff, dn, k)		)
      return (diff, dn)
def       __magic_name__(  __UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)						->    Tuple:



      '''simple docstring'''

      if i >= n:
            return 0, i
      if k > len(__UpperCAmelCase		):
            a_i.extend([0 for _ in range(k - len(__UpperCAmelCase		)		)]		)

      # note: a_i -> b * 10^k + c
      # ds_b -> digitsum(b)
      # ds_c -> digitsum(c)
      _lowerCamelCase			    =				i
      _lowerCamelCase    ,					_lowerCamelCase    ,					_lowerCamelCase			    =				0, 0, 0
      for j in range(len(__UpperCAmelCase		)		):
            if j >= k:
                  ds_b += a_i[j]
            else:
                  ds_c += a_i[j]

      while i < n:
            i += 1
            _lowerCamelCase			    =				ds_c + ds_b
            diff += addend
            _lowerCamelCase			    =				0
            for j in range(__UpperCAmelCase		):
                  _lowerCamelCase			    =				a_i[j] + addend
                  _lowerCamelCase    ,					_lowerCamelCase			    =				divmod(__UpperCAmelCase ,						10		)

                  ds_c += a_i[j]

            if addend > 0:
                  break

      if addend > 0:
            add(__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)
      return diff, i - start_i
def       __magic_name__(  __UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)						->    Tuple:



      '''simple docstring'''

      for j in range(__UpperCAmelCase ,						len(__UpperCAmelCase		)		):
            _lowerCamelCase			    =				digits[j] + addend
            if s >= 10:
                  _lowerCamelCase    ,					_lowerCamelCase			    =				divmod(__UpperCAmelCase ,						10		)
                  _lowerCamelCase			    =				addend // 10 + quotient
            else:
                  _lowerCamelCase			    =				s
                  _lowerCamelCase			    =				addend // 10

            if addend == 0:
                  break

      while addend > 0:
            _lowerCamelCase    ,					_lowerCamelCase			    =				divmod(__UpperCAmelCase ,						10		)
            digits.append(__UpperCAmelCase		)
def       __magic_name__(  __UpperCAmelCase = 10**15		)						->    int:



      '''simple docstring'''

      _lowerCamelCase			    =				[1]
      _lowerCamelCase			    =				1
      _lowerCamelCase			    =				0
      while True:
            _lowerCamelCase    ,					_lowerCamelCase			    =				next_term(__UpperCAmelCase ,						20 ,						i + dn ,						__UpperCAmelCase		)
            dn += terms_jumped
            if dn == n - i:
                  break

      _lowerCamelCase			    =				0
      for j in range(len(__UpperCAmelCase		)		):
            a_n += digits[j] * 10**j
      return a_n


if __name__ == "__main__":
     print(f'''{solution() = }''')
import logging

import numpy as np
import pytest
from scipy.linalg import eigh

logging.basicConfig(level=logging.INFO, format='%(message)s')
def       __magic_name__(  __UpperCAmelCase		)						->    np.ndarray:



      '''simple docstring'''

      return input_array.reshape((input_array.size, 1)		)
def       __magic_name__(  __UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)						->    np.ndarray:



      '''simple docstring'''

      _lowerCamelCase			    =				np.nan
      for i in range(__UpperCAmelCase		):
            _lowerCamelCase			    =				features[:, labels == i]
            _lowerCamelCase			    =				data.mean(1		)
            # Centralize the data of class i
            _lowerCamelCase			    =				data - column_reshape(__UpperCAmelCase		)
            if i > 0:
                  # If covariance_sum is not None
                  covariance_sum += np.dot(__UpperCAmelCase ,						centered_data.T		)
            else:
                  # If covariance_sum is np.nan (i.e. first loop)
                  _lowerCamelCase			    =				np.dot(__UpperCAmelCase ,						centered_data.T		)

      return covariance_sum / features.shape[1]
def       __magic_name__(  __UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)						->    np.ndarray:



      '''simple docstring'''

      _lowerCamelCase			    =				features.mean(1		)
      _lowerCamelCase			    =				np.nan
      for i in range(__UpperCAmelCase		):
            _lowerCamelCase			    =				features[:, labels == i]
            _lowerCamelCase			    =				data.shape[1]
            _lowerCamelCase			    =				data.mean(1		)
            if i > 0:
                  # If covariance_sum is not None
                  covariance_sum += device_data * np.dot(
                      column_reshape(__UpperCAmelCase		) - column_reshape(__UpperCAmelCase		) ,						(column_reshape(__UpperCAmelCase		) - column_reshape(__UpperCAmelCase		)).T ,						)
            else:
                  # If covariance_sum is np.nan (i.e. first loop)
                  _lowerCamelCase			    =				device_data * np.dot(
                      column_reshape(__UpperCAmelCase		) - column_reshape(__UpperCAmelCase		) ,						(column_reshape(__UpperCAmelCase		) - column_reshape(__UpperCAmelCase		)).T ,						)

      return covariance_sum / features.shape[1]
def       __magic_name__(  __UpperCAmelCase ,						__UpperCAmelCase		)						->    np.ndarray:



      '''simple docstring'''

      if features.any():
            _lowerCamelCase			    =				features.mean(1		)
            # Center the dataset
            _lowerCamelCase			    =				features - np.reshape(__UpperCAmelCase ,						(data_mean.size, 1)		)
            _lowerCamelCase			    =				np.dot(__UpperCAmelCase ,						centered_data.T		) / features.shape[1]
            _lowerCamelCase    ,					_lowerCamelCase			    =				np.linalg.eigh(__UpperCAmelCase		)
            # Take all the columns in the reverse order (-1), and then takes only the first
            _lowerCamelCase			    =				eigenvectors[:, ::-1][:, 0:dimensions]
            # Project the database on the new space
            _lowerCamelCase			    =				np.dot(filtered_eigenvectors.T ,						__UpperCAmelCase		)
            logging.info('''Principal Component Analysis computed'''		)

            return projected_data
      else:
            logging.basicConfig(level=logging.ERROR ,						format='''%(message)s''' ,						force=__UpperCAmelCase		)
            logging.error('''Dataset empty'''		)
            raise AssertionError
def       __magic_name__(  __UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)						->    np.ndarray:



      '''simple docstring'''

      assert classes > dimensions

      # Check if features have been already loaded
      if features.any:
            _lowerCamelCase    ,					_lowerCamelCase			    =				eigh(
                covariance_between_classes(__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		) ,						covariance_within_classes(__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		) ,						)
            _lowerCamelCase			    =				eigenvectors[:, ::-1][:, :dimensions]
            _lowerCamelCase    ,					_lowerCamelCase    ,					_lowerCamelCase			    =				np.linalg.svd(__UpperCAmelCase		)
            _lowerCamelCase			    =				svd_matrix[:, 0:dimensions]
            _lowerCamelCase			    =				np.dot(filtered_svd_matrix.T ,						__UpperCAmelCase		)
            logging.info('''Linear Discriminant Analysis computed'''		)

            return projected_data
      else:
            logging.basicConfig(level=logging.ERROR ,						format='''%(message)s''' ,						force=__UpperCAmelCase		)
            logging.error('''Dataset empty'''		)
            raise AssertionError
def       __magic_name__(  )						->    None:



      '''simple docstring'''

      _lowerCamelCase			    =				np.array([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]]		)
      _lowerCamelCase			    =				np.array([0, 0, 0, 1, 1]		)
      _lowerCamelCase			    =				2
      _lowerCamelCase			    =				2

      # Assert that the function raises an AssertionError if dimensions > classes
      with pytest.raises(__UpperCAmelCase		) as error_info:
            _lowerCamelCase			    =				linear_discriminant_analysis(
                __UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase ,						__UpperCAmelCase		)
            if isinstance(__UpperCAmelCase ,						np.ndarray		):
                  raise AssertionError(
                      '''Did not raise AssertionError for dimensions > classes'''		)
            assert error_info.type is AssertionError
def       __magic_name__(  )						->    None:



      '''simple docstring'''

      _lowerCamelCase			    =				np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]		)
      _lowerCamelCase			    =				2
      _lowerCamelCase			    =				np.array([[6.9_2_8_2_0_3_2_3, 8.6_6_0_2_5_4_0_4, 1_0.3_9_2_3_0_4_8_5], [3.0, 3.0, 3.0]]		)

      with pytest.raises(__UpperCAmelCase		) as error_info:
            _lowerCamelCase			    =				principal_component_analysis(__UpperCAmelCase ,						__UpperCAmelCase		)
            if not np.allclose(__UpperCAmelCase ,						__UpperCAmelCase		):
                  raise AssertionError
            assert error_info.type is AssertionError


if __name__ == "__main__":
     import doctest

     doctest.testmod()