diophantine.py 117 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005
  1. from sympy.core.add import Add
  2. from sympy.core.assumptions import check_assumptions
  3. from sympy.core.containers import Tuple
  4. from sympy.core.exprtools import factor_terms
  5. from sympy.core.function import _mexpand
  6. from sympy.core.mul import Mul
  7. from sympy.core.numbers import Rational
  8. from sympy.core.numbers import igcdex, ilcm, igcd
  9. from sympy.core.power import integer_nthroot, isqrt
  10. from sympy.core.relational import Eq
  11. from sympy.core.singleton import S
  12. from sympy.core.sorting import default_sort_key, ordered
  13. from sympy.core.symbol import Symbol, symbols
  14. from sympy.core.sympify import _sympify
  15. from sympy.functions.elementary.complexes import sign
  16. from sympy.functions.elementary.integers import floor
  17. from sympy.functions.elementary.miscellaneous import sqrt
  18. from sympy.matrices.dense import MutableDenseMatrix as Matrix
  19. from sympy.ntheory.factor_ import (
  20. divisors, factorint, multiplicity, perfect_power)
  21. from sympy.ntheory.generate import nextprime
  22. from sympy.ntheory.primetest import is_square, isprime
  23. from sympy.ntheory.residue_ntheory import sqrt_mod
  24. from sympy.polys.polyerrors import GeneratorsNeeded
  25. from sympy.polys.polytools import Poly, factor_list
  26. from sympy.simplify.simplify import signsimp
  27. from sympy.solvers.solveset import solveset_real
  28. from sympy.utilities import numbered_symbols
  29. from sympy.utilities.misc import as_int, filldedent
  30. from sympy.utilities.iterables import (is_sequence, subsets, permute_signs,
  31. signed_permutations, ordered_partitions)
  32. # these are imported with 'from sympy.solvers.diophantine import *
  33. __all__ = ['diophantine', 'classify_diop']
  34. class DiophantineSolutionSet(set):
  35. """
  36. Container for a set of solutions to a particular diophantine equation.
  37. The base representation is a set of tuples representing each of the solutions.
  38. Parameters
  39. ==========
  40. symbols : list
  41. List of free symbols in the original equation.
  42. parameters: list
  43. List of parameters to be used in the solution.
  44. Examples
  45. ========
  46. Adding solutions:
  47. >>> from sympy.solvers.diophantine.diophantine import DiophantineSolutionSet
  48. >>> from sympy.abc import x, y, t, u
  49. >>> s1 = DiophantineSolutionSet([x, y], [t, u])
  50. >>> s1
  51. set()
  52. >>> s1.add((2, 3))
  53. >>> s1.add((-1, u))
  54. >>> s1
  55. {(-1, u), (2, 3)}
  56. >>> s2 = DiophantineSolutionSet([x, y], [t, u])
  57. >>> s2.add((3, 4))
  58. >>> s1.update(*s2)
  59. >>> s1
  60. {(-1, u), (2, 3), (3, 4)}
  61. Conversion of solutions into dicts:
  62. >>> list(s1.dict_iterator())
  63. [{x: -1, y: u}, {x: 2, y: 3}, {x: 3, y: 4}]
  64. Substituting values:
  65. >>> s3 = DiophantineSolutionSet([x, y], [t, u])
  66. >>> s3.add((t**2, t + u))
  67. >>> s3
  68. {(t**2, t + u)}
  69. >>> s3.subs({t: 2, u: 3})
  70. {(4, 5)}
  71. >>> s3.subs(t, -1)
  72. {(1, u - 1)}
  73. >>> s3.subs(t, 3)
  74. {(9, u + 3)}
  75. Evaluation at specific values. Positional arguments are given in the same order as the parameters:
  76. >>> s3(-2, 3)
  77. {(4, 1)}
  78. >>> s3(5)
  79. {(25, u + 5)}
  80. >>> s3(None, 2)
  81. {(t**2, t + 2)}
  82. """
  83. def __init__(self, symbols_seq, parameters):
  84. super().__init__()
  85. if not is_sequence(symbols_seq):
  86. raise ValueError("Symbols must be given as a sequence.")
  87. if not is_sequence(parameters):
  88. raise ValueError("Parameters must be given as a sequence.")
  89. self.symbols = tuple(symbols_seq)
  90. self.parameters = tuple(parameters)
  91. def add(self, solution):
  92. if len(solution) != len(self.symbols):
  93. raise ValueError("Solution should have a length of %s, not %s" % (len(self.symbols), len(solution)))
  94. super().add(Tuple(*solution))
  95. def update(self, *solutions):
  96. for solution in solutions:
  97. self.add(solution)
  98. def dict_iterator(self):
  99. for solution in ordered(self):
  100. yield dict(zip(self.symbols, solution))
  101. def subs(self, *args, **kwargs):
  102. result = DiophantineSolutionSet(self.symbols, self.parameters)
  103. for solution in self:
  104. result.add(solution.subs(*args, **kwargs))
  105. return result
  106. def __call__(self, *args):
  107. if len(args) > len(self.parameters):
  108. raise ValueError("Evaluation should have at most %s values, not %s" % (len(self.parameters), len(args)))
  109. rep = {p: v for p, v in zip(self.parameters, args) if v is not None}
  110. return self.subs(rep)
  111. class DiophantineEquationType:
  112. """
  113. Internal representation of a particular diophantine equation type.
  114. Parameters
  115. ==========
  116. equation :
  117. The diophantine equation that is being solved.
  118. free_symbols : list (optional)
  119. The symbols being solved for.
  120. Attributes
  121. ==========
  122. total_degree :
  123. The maximum of the degrees of all terms in the equation
  124. homogeneous :
  125. Does the equation contain a term of degree 0
  126. homogeneous_order :
  127. Does the equation contain any coefficient that is in the symbols being solved for
  128. dimension :
  129. The number of symbols being solved for
  130. """
  131. name = None # type: str
  132. def __init__(self, equation, free_symbols=None):
  133. self.equation = _sympify(equation).expand(force=True)
  134. if free_symbols is not None:
  135. self.free_symbols = free_symbols
  136. else:
  137. self.free_symbols = list(self.equation.free_symbols)
  138. self.free_symbols.sort(key=default_sort_key)
  139. if not self.free_symbols:
  140. raise ValueError('equation should have 1 or more free symbols')
  141. self.coeff = self.equation.as_coefficients_dict()
  142. if not all(_is_int(c) for c in self.coeff.values()):
  143. raise TypeError("Coefficients should be Integers")
  144. self.total_degree = Poly(self.equation).total_degree()
  145. self.homogeneous = 1 not in self.coeff
  146. self.homogeneous_order = not (set(self.coeff) & set(self.free_symbols))
  147. self.dimension = len(self.free_symbols)
  148. self._parameters = None
  149. def matches(self):
  150. """
  151. Determine whether the given equation can be matched to the particular equation type.
  152. """
  153. return False
  154. @property
  155. def n_parameters(self):
  156. return self.dimension
  157. @property
  158. def parameters(self):
  159. if self._parameters is None:
  160. self._parameters = symbols('t_:%i' % (self.n_parameters,), integer=True)
  161. return self._parameters
  162. def solve(self, parameters=None, limit=None) -> DiophantineSolutionSet:
  163. raise NotImplementedError('No solver has been written for %s.' % self.name)
  164. def pre_solve(self, parameters=None):
  165. if not self.matches():
  166. raise ValueError("This equation does not match the %s equation type." % self.name)
  167. if parameters is not None:
  168. if len(parameters) != self.n_parameters:
  169. raise ValueError("Expected %s parameter(s) but got %s" % (self.n_parameters, len(parameters)))
  170. self._parameters = parameters
  171. class Univariate(DiophantineEquationType):
  172. """
  173. Representation of a univariate diophantine equation.
  174. A univariate diophantine equation is an equation of the form
  175. `a_{0} + a_{1}x + a_{2}x^2 + .. + a_{n}x^n = 0` where `a_{1}, a_{2}, ..a_{n}` are
  176. integer constants and `x` is an integer variable.
  177. Examples
  178. ========
  179. >>> from sympy.solvers.diophantine.diophantine import Univariate
  180. >>> from sympy.abc import x
  181. >>> Univariate((x - 2)*(x - 3)**2).solve() # solves equation (x - 2)*(x - 3)**2 == 0
  182. {(2,), (3,)}
  183. """
  184. name = 'univariate'
  185. def matches(self):
  186. return self.dimension == 1
  187. def solve(self, parameters=None, limit=None):
  188. self.pre_solve(parameters)
  189. result = DiophantineSolutionSet(self.free_symbols, parameters=self.parameters)
  190. for i in solveset_real(self.equation, self.free_symbols[0]).intersect(S.Integers):
  191. result.add((i,))
  192. return result
  193. class Linear(DiophantineEquationType):
  194. """
  195. Representation of a linear diophantine equation.
  196. A linear diophantine equation is an equation of the form `a_{1}x_{1} +
  197. a_{2}x_{2} + .. + a_{n}x_{n} = 0` where `a_{1}, a_{2}, ..a_{n}` are
  198. integer constants and `x_{1}, x_{2}, ..x_{n}` are integer variables.
  199. Examples
  200. ========
  201. >>> from sympy.solvers.diophantine.diophantine import Linear
  202. >>> from sympy.abc import x, y, z
  203. >>> l1 = Linear(2*x - 3*y - 5)
  204. >>> l1.matches() # is this equation linear
  205. True
  206. >>> l1.solve() # solves equation 2*x - 3*y - 5 == 0
  207. {(3*t_0 - 5, 2*t_0 - 5)}
  208. Here x = -3*t_0 - 5 and y = -2*t_0 - 5
  209. >>> Linear(2*x - 3*y - 4*z -3).solve()
  210. {(t_0, 2*t_0 + 4*t_1 + 3, -t_0 - 3*t_1 - 3)}
  211. """
  212. name = 'linear'
  213. def matches(self):
  214. return self.total_degree == 1
  215. def solve(self, parameters=None, limit=None):
  216. self.pre_solve(parameters)
  217. coeff = self.coeff
  218. var = self.free_symbols
  219. if 1 in coeff:
  220. # negate coeff[] because input is of the form: ax + by + c == 0
  221. # but is used as: ax + by == -c
  222. c = -coeff[1]
  223. else:
  224. c = 0
  225. result = DiophantineSolutionSet(var, parameters=self.parameters)
  226. params = result.parameters
  227. if len(var) == 1:
  228. q, r = divmod(c, coeff[var[0]])
  229. if not r:
  230. result.add((q,))
  231. return result
  232. else:
  233. return result
  234. '''
  235. base_solution_linear() can solve diophantine equations of the form:
  236. a*x + b*y == c
  237. We break down multivariate linear diophantine equations into a
  238. series of bivariate linear diophantine equations which can then
  239. be solved individually by base_solution_linear().
  240. Consider the following:
  241. a_0*x_0 + a_1*x_1 + a_2*x_2 == c
  242. which can be re-written as:
  243. a_0*x_0 + g_0*y_0 == c
  244. where
  245. g_0 == gcd(a_1, a_2)
  246. and
  247. y == (a_1*x_1)/g_0 + (a_2*x_2)/g_0
  248. This leaves us with two binary linear diophantine equations.
  249. For the first equation:
  250. a == a_0
  251. b == g_0
  252. c == c
  253. For the second:
  254. a == a_1/g_0
  255. b == a_2/g_0
  256. c == the solution we find for y_0 in the first equation.
  257. The arrays A and B are the arrays of integers used for
  258. 'a' and 'b' in each of the n-1 bivariate equations we solve.
  259. '''
  260. A = [coeff[v] for v in var]
  261. B = []
  262. if len(var) > 2:
  263. B.append(igcd(A[-2], A[-1]))
  264. A[-2] = A[-2] // B[0]
  265. A[-1] = A[-1] // B[0]
  266. for i in range(len(A) - 3, 0, -1):
  267. gcd = igcd(B[0], A[i])
  268. B[0] = B[0] // gcd
  269. A[i] = A[i] // gcd
  270. B.insert(0, gcd)
  271. B.append(A[-1])
  272. '''
  273. Consider the trivariate linear equation:
  274. 4*x_0 + 6*x_1 + 3*x_2 == 2
  275. This can be re-written as:
  276. 4*x_0 + 3*y_0 == 2
  277. where
  278. y_0 == 2*x_1 + x_2
  279. (Note that gcd(3, 6) == 3)
  280. The complete integral solution to this equation is:
  281. x_0 == 2 + 3*t_0
  282. y_0 == -2 - 4*t_0
  283. where 't_0' is any integer.
  284. Now that we have a solution for 'x_0', find 'x_1' and 'x_2':
  285. 2*x_1 + x_2 == -2 - 4*t_0
  286. We can then solve for '-2' and '-4' independently,
  287. and combine the results:
  288. 2*x_1a + x_2a == -2
  289. x_1a == 0 + t_0
  290. x_2a == -2 - 2*t_0
  291. 2*x_1b + x_2b == -4*t_0
  292. x_1b == 0*t_0 + t_1
  293. x_2b == -4*t_0 - 2*t_1
  294. ==>
  295. x_1 == t_0 + t_1
  296. x_2 == -2 - 6*t_0 - 2*t_1
  297. where 't_0' and 't_1' are any integers.
  298. Note that:
  299. 4*(2 + 3*t_0) + 6*(t_0 + t_1) + 3*(-2 - 6*t_0 - 2*t_1) == 2
  300. for any integral values of 't_0', 't_1'; as required.
  301. This method is generalised for many variables, below.
  302. '''
  303. solutions = []
  304. for Ai, Bi in zip(A, B):
  305. tot_x, tot_y = [], []
  306. for j, arg in enumerate(Add.make_args(c)):
  307. if arg.is_Integer:
  308. # example: 5 -> k = 5
  309. k, p = arg, S.One
  310. pnew = params[0]
  311. else: # arg is a Mul or Symbol
  312. # example: 3*t_1 -> k = 3
  313. # example: t_0 -> k = 1
  314. k, p = arg.as_coeff_Mul()
  315. pnew = params[params.index(p) + 1]
  316. sol = sol_x, sol_y = base_solution_linear(k, Ai, Bi, pnew)
  317. if p is S.One:
  318. if None in sol:
  319. return result
  320. else:
  321. # convert a + b*pnew -> a*p + b*pnew
  322. if isinstance(sol_x, Add):
  323. sol_x = sol_x.args[0]*p + sol_x.args[1]
  324. if isinstance(sol_y, Add):
  325. sol_y = sol_y.args[0]*p + sol_y.args[1]
  326. tot_x.append(sol_x)
  327. tot_y.append(sol_y)
  328. solutions.append(Add(*tot_x))
  329. c = Add(*tot_y)
  330. solutions.append(c)
  331. result.add(solutions)
  332. return result
  333. class BinaryQuadratic(DiophantineEquationType):
  334. """
  335. Representation of a binary quadratic diophantine equation.
  336. A binary quadratic diophantine equation is an equation of the
  337. form `Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0`, where `A, B, C, D, E,
  338. F` are integer constants and `x` and `y` are integer variables.
  339. Examples
  340. ========
  341. >>> from sympy.abc import x, y
  342. >>> from sympy.solvers.diophantine.diophantine import BinaryQuadratic
  343. >>> b1 = BinaryQuadratic(x**3 + y**2 + 1)
  344. >>> b1.matches()
  345. False
  346. >>> b2 = BinaryQuadratic(x**2 + y**2 + 2*x + 2*y + 2)
  347. >>> b2.matches()
  348. True
  349. >>> b2.solve()
  350. {(-1, -1)}
  351. References
  352. ==========
  353. .. [1] Methods to solve Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0, [online],
  354. Available: https://www.alpertron.com.ar/METHODS.HTM
  355. .. [2] Solving the equation ax^2+ bxy + cy^2 + dx + ey + f= 0, [online],
  356. Available: https://web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
  357. """
  358. name = 'binary_quadratic'
  359. def matches(self):
  360. return self.total_degree == 2 and self.dimension == 2
  361. def solve(self, parameters=None, limit=None) -> DiophantineSolutionSet:
  362. self.pre_solve(parameters)
  363. var = self.free_symbols
  364. coeff = self.coeff
  365. x, y = var
  366. A = coeff[x**2]
  367. B = coeff[x*y]
  368. C = coeff[y**2]
  369. D = coeff[x]
  370. E = coeff[y]
  371. F = coeff[S.One]
  372. A, B, C, D, E, F = [as_int(i) for i in _remove_gcd(A, B, C, D, E, F)]
  373. # (1) Simple-Hyperbolic case: A = C = 0, B != 0
  374. # In this case equation can be converted to (Bx + E)(By + D) = DE - BF
  375. # We consider two cases; DE - BF = 0 and DE - BF != 0
  376. # More details, https://www.alpertron.com.ar/METHODS.HTM#SHyperb
  377. result = DiophantineSolutionSet(var, self.parameters)
  378. t, u = result.parameters
  379. discr = B**2 - 4*A*C
  380. if A == 0 and C == 0 and B != 0:
  381. if D*E - B*F == 0:
  382. q, r = divmod(E, B)
  383. if not r:
  384. result.add((-q, t))
  385. q, r = divmod(D, B)
  386. if not r:
  387. result.add((t, -q))
  388. else:
  389. div = divisors(D*E - B*F)
  390. div = div + [-term for term in div]
  391. for d in div:
  392. x0, r = divmod(d - E, B)
  393. if not r:
  394. q, r = divmod(D*E - B*F, d)
  395. if not r:
  396. y0, r = divmod(q - D, B)
  397. if not r:
  398. result.add((x0, y0))
  399. # (2) Parabolic case: B**2 - 4*A*C = 0
  400. # There are two subcases to be considered in this case.
  401. # sqrt(c)D - sqrt(a)E = 0 and sqrt(c)D - sqrt(a)E != 0
  402. # More Details, https://www.alpertron.com.ar/METHODS.HTM#Parabol
  403. elif discr == 0:
  404. if A == 0:
  405. s = BinaryQuadratic(self.equation, free_symbols=[y, x]).solve(parameters=[t, u])
  406. for soln in s:
  407. result.add((soln[1], soln[0]))
  408. else:
  409. g = sign(A)*igcd(A, C)
  410. a = A // g
  411. c = C // g
  412. e = sign(B / A)
  413. sqa = isqrt(a)
  414. sqc = isqrt(c)
  415. _c = e*sqc*D - sqa*E
  416. if not _c:
  417. z = Symbol("z", real=True)
  418. eq = sqa*g*z**2 + D*z + sqa*F
  419. roots = solveset_real(eq, z).intersect(S.Integers)
  420. for root in roots:
  421. ans = diop_solve(sqa*x + e*sqc*y - root)
  422. result.add((ans[0], ans[1]))
  423. elif _is_int(c):
  424. solve_x = lambda u: -e*sqc*g*_c*t**2 - (E + 2*e*sqc*g*u)*t \
  425. - (e*sqc*g*u**2 + E*u + e*sqc*F) // _c
  426. solve_y = lambda u: sqa*g*_c*t**2 + (D + 2*sqa*g*u)*t \
  427. + (sqa*g*u**2 + D*u + sqa*F) // _c
  428. for z0 in range(0, abs(_c)):
  429. # Check if the coefficients of y and x obtained are integers or not
  430. if (divisible(sqa*g*z0**2 + D*z0 + sqa*F, _c) and
  431. divisible(e*sqc*g*z0**2 + E*z0 + e*sqc*F, _c)):
  432. result.add((solve_x(z0), solve_y(z0)))
  433. # (3) Method used when B**2 - 4*A*C is a square, is described in p. 6 of the below paper
  434. # by John P. Robertson.
  435. # https://web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
  436. elif is_square(discr):
  437. if A != 0:
  438. r = sqrt(discr)
  439. u, v = symbols("u, v", integer=True)
  440. eq = _mexpand(
  441. 4*A*r*u*v + 4*A*D*(B*v + r*u + r*v - B*u) +
  442. 2*A*4*A*E*(u - v) + 4*A*r*4*A*F)
  443. solution = diop_solve(eq, t)
  444. for s0, t0 in solution:
  445. num = B*t0 + r*s0 + r*t0 - B*s0
  446. x_0 = S(num) / (4*A*r)
  447. y_0 = S(s0 - t0) / (2*r)
  448. if isinstance(s0, Symbol) or isinstance(t0, Symbol):
  449. if len(check_param(x_0, y_0, 4*A*r, parameters)) > 0:
  450. ans = check_param(x_0, y_0, 4*A*r, parameters)
  451. result.update(*ans)
  452. elif x_0.is_Integer and y_0.is_Integer:
  453. if is_solution_quad(var, coeff, x_0, y_0):
  454. result.add((x_0, y_0))
  455. else:
  456. s = BinaryQuadratic(self.equation, free_symbols=var[::-1]).solve(parameters=[t, u]) # Interchange x and y
  457. while s:
  458. result.add(s.pop()[::-1]) # and solution <--------+
  459. # (4) B**2 - 4*A*C > 0 and B**2 - 4*A*C not a square or B**2 - 4*A*C < 0
  460. else:
  461. P, Q = _transformation_to_DN(var, coeff)
  462. D, N = _find_DN(var, coeff)
  463. solns_pell = diop_DN(D, N)
  464. if D < 0:
  465. for x0, y0 in solns_pell:
  466. for x in [-x0, x0]:
  467. for y in [-y0, y0]:
  468. s = P*Matrix([x, y]) + Q
  469. try:
  470. result.add([as_int(_) for _ in s])
  471. except ValueError:
  472. pass
  473. else:
  474. # In this case equation can be transformed into a Pell equation
  475. solns_pell = set(solns_pell)
  476. for X, Y in list(solns_pell):
  477. solns_pell.add((-X, -Y))
  478. a = diop_DN(D, 1)
  479. T = a[0][0]
  480. U = a[0][1]
  481. if all(_is_int(_) for _ in P[:4] + Q[:2]):
  482. for r, s in solns_pell:
  483. _a = (r + s*sqrt(D))*(T + U*sqrt(D))**t
  484. _b = (r - s*sqrt(D))*(T - U*sqrt(D))**t
  485. x_n = _mexpand(S(_a + _b) / 2)
  486. y_n = _mexpand(S(_a - _b) / (2*sqrt(D)))
  487. s = P*Matrix([x_n, y_n]) + Q
  488. result.add(s)
  489. else:
  490. L = ilcm(*[_.q for _ in P[:4] + Q[:2]])
  491. k = 1
  492. T_k = T
  493. U_k = U
  494. while (T_k - 1) % L != 0 or U_k % L != 0:
  495. T_k, U_k = T_k*T + D*U_k*U, T_k*U + U_k*T
  496. k += 1
  497. for X, Y in solns_pell:
  498. for i in range(k):
  499. if all(_is_int(_) for _ in P*Matrix([X, Y]) + Q):
  500. _a = (X + sqrt(D)*Y)*(T_k + sqrt(D)*U_k)**t
  501. _b = (X - sqrt(D)*Y)*(T_k - sqrt(D)*U_k)**t
  502. Xt = S(_a + _b) / 2
  503. Yt = S(_a - _b) / (2*sqrt(D))
  504. s = P*Matrix([Xt, Yt]) + Q
  505. result.add(s)
  506. X, Y = X*T + D*U*Y, X*U + Y*T
  507. return result
  508. class InhomogeneousTernaryQuadratic(DiophantineEquationType):
  509. """
  510. Representation of an inhomogeneous ternary quadratic.
  511. No solver is currently implemented for this equation type.
  512. """
  513. name = 'inhomogeneous_ternary_quadratic'
  514. def matches(self):
  515. if not (self.total_degree == 2 and self.dimension == 3):
  516. return False
  517. if not self.homogeneous:
  518. return False
  519. return not self.homogeneous_order
  520. class HomogeneousTernaryQuadraticNormal(DiophantineEquationType):
  521. """
  522. Representation of a homogeneous ternary quadratic normal diophantine equation.
  523. Examples
  524. ========
  525. >>> from sympy.abc import x, y, z
  526. >>> from sympy.solvers.diophantine.diophantine import HomogeneousTernaryQuadraticNormal
  527. >>> HomogeneousTernaryQuadraticNormal(4*x**2 - 5*y**2 + z**2).solve()
  528. {(1, 2, 4)}
  529. """
  530. name = 'homogeneous_ternary_quadratic_normal'
  531. def matches(self):
  532. if not (self.total_degree == 2 and self.dimension == 3):
  533. return False
  534. if not self.homogeneous:
  535. return False
  536. if not self.homogeneous_order:
  537. return False
  538. nonzero = [k for k in self.coeff if self.coeff[k]]
  539. return len(nonzero) == 3 and all(i**2 in nonzero for i in self.free_symbols)
  540. def solve(self, parameters=None, limit=None) -> DiophantineSolutionSet:
  541. self.pre_solve(parameters)
  542. var = self.free_symbols
  543. coeff = self.coeff
  544. x, y, z = var
  545. a = coeff[x**2]
  546. b = coeff[y**2]
  547. c = coeff[z**2]
  548. (sqf_of_a, sqf_of_b, sqf_of_c), (a_1, b_1, c_1), (a_2, b_2, c_2) = \
  549. sqf_normal(a, b, c, steps=True)
  550. A = -a_2*c_2
  551. B = -b_2*c_2
  552. result = DiophantineSolutionSet(var, parameters=self.parameters)
  553. # If following two conditions are satisfied then there are no solutions
  554. if A < 0 and B < 0:
  555. return result
  556. if (
  557. sqrt_mod(-b_2*c_2, a_2) is None or
  558. sqrt_mod(-c_2*a_2, b_2) is None or
  559. sqrt_mod(-a_2*b_2, c_2) is None):
  560. return result
  561. z_0, x_0, y_0 = descent(A, B)
  562. z_0, q = _rational_pq(z_0, abs(c_2))
  563. x_0 *= q
  564. y_0 *= q
  565. x_0, y_0, z_0 = _remove_gcd(x_0, y_0, z_0)
  566. # Holzer reduction
  567. if sign(a) == sign(b):
  568. x_0, y_0, z_0 = holzer(x_0, y_0, z_0, abs(a_2), abs(b_2), abs(c_2))
  569. elif sign(a) == sign(c):
  570. x_0, z_0, y_0 = holzer(x_0, z_0, y_0, abs(a_2), abs(c_2), abs(b_2))
  571. else:
  572. y_0, z_0, x_0 = holzer(y_0, z_0, x_0, abs(b_2), abs(c_2), abs(a_2))
  573. x_0 = reconstruct(b_1, c_1, x_0)
  574. y_0 = reconstruct(a_1, c_1, y_0)
  575. z_0 = reconstruct(a_1, b_1, z_0)
  576. sq_lcm = ilcm(sqf_of_a, sqf_of_b, sqf_of_c)
  577. x_0 = abs(x_0*sq_lcm // sqf_of_a)
  578. y_0 = abs(y_0*sq_lcm // sqf_of_b)
  579. z_0 = abs(z_0*sq_lcm // sqf_of_c)
  580. result.add(_remove_gcd(x_0, y_0, z_0))
  581. return result
  582. class HomogeneousTernaryQuadratic(DiophantineEquationType):
  583. """
  584. Representation of a homogeneous ternary quadratic diophantine equation.
  585. Examples
  586. ========
  587. >>> from sympy.abc import x, y, z
  588. >>> from sympy.solvers.diophantine.diophantine import HomogeneousTernaryQuadratic
  589. >>> HomogeneousTernaryQuadratic(x**2 + y**2 - 3*z**2 + x*y).solve()
  590. {(-1, 2, 1)}
  591. >>> HomogeneousTernaryQuadratic(3*x**2 + y**2 - 3*z**2 + 5*x*y + y*z).solve()
  592. {(3, 12, 13)}
  593. """
  594. name = 'homogeneous_ternary_quadratic'
  595. def matches(self):
  596. if not (self.total_degree == 2 and self.dimension == 3):
  597. return False
  598. if not self.homogeneous:
  599. return False
  600. if not self.homogeneous_order:
  601. return False
  602. nonzero = [k for k in self.coeff if self.coeff[k]]
  603. return not (len(nonzero) == 3 and all(i**2 in nonzero for i in self.free_symbols))
  604. def solve(self, parameters=None, limit=None):
  605. self.pre_solve(parameters)
  606. _var = self.free_symbols
  607. coeff = self.coeff
  608. x, y, z = _var
  609. var = [x, y, z]
  610. # Equations of the form B*x*y + C*z*x + E*y*z = 0 and At least two of the
  611. # coefficients A, B, C are non-zero.
  612. # There are infinitely many solutions for the equation.
  613. # Ex: (0, 0, t), (0, t, 0), (t, 0, 0)
  614. # Equation can be re-written as y*(B*x + E*z) = -C*x*z and we can find rather
  615. # unobvious solutions. Set y = -C and B*x + E*z = x*z. The latter can be solved by
  616. # using methods for binary quadratic diophantine equations. Let's select the
  617. # solution which minimizes |x| + |z|
  618. result = DiophantineSolutionSet(var, parameters=self.parameters)
  619. def unpack_sol(sol):
  620. if len(sol) > 0:
  621. return list(sol)[0]
  622. return None, None, None
  623. if not any(coeff[i**2] for i in var):
  624. if coeff[x*z]:
  625. sols = diophantine(coeff[x*y]*x + coeff[y*z]*z - x*z)
  626. s = sols.pop()
  627. min_sum = abs(s[0]) + abs(s[1])
  628. for r in sols:
  629. m = abs(r[0]) + abs(r[1])
  630. if m < min_sum:
  631. s = r
  632. min_sum = m
  633. result.add(_remove_gcd(s[0], -coeff[x*z], s[1]))
  634. return result
  635. else:
  636. var[0], var[1] = _var[1], _var[0]
  637. y_0, x_0, z_0 = unpack_sol(_diop_ternary_quadratic(var, coeff))
  638. if x_0 is not None:
  639. result.add((x_0, y_0, z_0))
  640. return result
  641. if coeff[x**2] == 0:
  642. # If the coefficient of x is zero change the variables
  643. if coeff[y**2] == 0:
  644. var[0], var[2] = _var[2], _var[0]
  645. z_0, y_0, x_0 = unpack_sol(_diop_ternary_quadratic(var, coeff))
  646. else:
  647. var[0], var[1] = _var[1], _var[0]
  648. y_0, x_0, z_0 = unpack_sol(_diop_ternary_quadratic(var, coeff))
  649. else:
  650. if coeff[x*y] or coeff[x*z]:
  651. # Apply the transformation x --> X - (B*y + C*z)/(2*A)
  652. A = coeff[x**2]
  653. B = coeff[x*y]
  654. C = coeff[x*z]
  655. D = coeff[y**2]
  656. E = coeff[y*z]
  657. F = coeff[z**2]
  658. _coeff = {}
  659. _coeff[x**2] = 4*A**2
  660. _coeff[y**2] = 4*A*D - B**2
  661. _coeff[z**2] = 4*A*F - C**2
  662. _coeff[y*z] = 4*A*E - 2*B*C
  663. _coeff[x*y] = 0
  664. _coeff[x*z] = 0
  665. x_0, y_0, z_0 = unpack_sol(_diop_ternary_quadratic(var, _coeff))
  666. if x_0 is None:
  667. return result
  668. p, q = _rational_pq(B*y_0 + C*z_0, 2*A)
  669. x_0, y_0, z_0 = x_0*q - p, y_0*q, z_0*q
  670. elif coeff[z*y] != 0:
  671. if coeff[y**2] == 0:
  672. if coeff[z**2] == 0:
  673. # Equations of the form A*x**2 + E*yz = 0.
  674. A = coeff[x**2]
  675. E = coeff[y*z]
  676. b, a = _rational_pq(-E, A)
  677. x_0, y_0, z_0 = b, a, b
  678. else:
  679. # Ax**2 + E*y*z + F*z**2 = 0
  680. var[0], var[2] = _var[2], _var[0]
  681. z_0, y_0, x_0 = unpack_sol(_diop_ternary_quadratic(var, coeff))
  682. else:
  683. # A*x**2 + D*y**2 + E*y*z + F*z**2 = 0, C may be zero
  684. var[0], var[1] = _var[1], _var[0]
  685. y_0, x_0, z_0 = unpack_sol(_diop_ternary_quadratic(var, coeff))
  686. else:
  687. # Ax**2 + D*y**2 + F*z**2 = 0, C may be zero
  688. x_0, y_0, z_0 = unpack_sol(_diop_ternary_quadratic_normal(var, coeff))
  689. if x_0 is None:
  690. return result
  691. result.add(_remove_gcd(x_0, y_0, z_0))
  692. return result
  693. class InhomogeneousGeneralQuadratic(DiophantineEquationType):
  694. """
  695. Representation of an inhomogeneous general quadratic.
  696. No solver is currently implemented for this equation type.
  697. """
  698. name = 'inhomogeneous_general_quadratic'
  699. def matches(self):
  700. if not (self.total_degree == 2 and self.dimension >= 3):
  701. return False
  702. if not self.homogeneous_order:
  703. return True
  704. else:
  705. # there may be Pow keys like x**2 or Mul keys like x*y
  706. if any(k.is_Mul for k in self.coeff): # cross terms
  707. return not self.homogeneous
  708. return False
  709. class HomogeneousGeneralQuadratic(DiophantineEquationType):
  710. """
  711. Representation of a homogeneous general quadratic.
  712. No solver is currently implemented for this equation type.
  713. """
  714. name = 'homogeneous_general_quadratic'
  715. def matches(self):
  716. if not (self.total_degree == 2 and self.dimension >= 3):
  717. return False
  718. if not self.homogeneous_order:
  719. return False
  720. else:
  721. # there may be Pow keys like x**2 or Mul keys like x*y
  722. if any(k.is_Mul for k in self.coeff): # cross terms
  723. return self.homogeneous
  724. return False
  725. class GeneralSumOfSquares(DiophantineEquationType):
  726. r"""
  727. Representation of the diophantine equation
  728. `x_{1}^2 + x_{2}^2 + . . . + x_{n}^2 - k = 0`.
  729. Details
  730. =======
  731. When `n = 3` if `k = 4^a(8m + 7)` for some `a, m \in Z` then there will be
  732. no solutions. Refer [1]_ for more details.
  733. Examples
  734. ========
  735. >>> from sympy.solvers.diophantine.diophantine import GeneralSumOfSquares
  736. >>> from sympy.abc import a, b, c, d, e
  737. >>> GeneralSumOfSquares(a**2 + b**2 + c**2 + d**2 + e**2 - 2345).solve()
  738. {(15, 22, 22, 24, 24)}
  739. By default only 1 solution is returned. Use the `limit` keyword for more:
  740. >>> sorted(GeneralSumOfSquares(a**2 + b**2 + c**2 + d**2 + e**2 - 2345).solve(limit=3))
  741. [(15, 22, 22, 24, 24), (16, 19, 24, 24, 24), (16, 20, 22, 23, 26)]
  742. References
  743. ==========
  744. .. [1] Representing an integer as a sum of three squares, [online],
  745. Available:
  746. https://www.proofwiki.org/wiki/Integer_as_Sum_of_Three_Squares
  747. """
  748. name = 'general_sum_of_squares'
  749. def matches(self):
  750. if not (self.total_degree == 2 and self.dimension >= 3):
  751. return False
  752. if not self.homogeneous_order:
  753. return False
  754. if any(k.is_Mul for k in self.coeff):
  755. return False
  756. return all(self.coeff[k] == 1 for k in self.coeff if k != 1)
  757. def solve(self, parameters=None, limit=1):
  758. self.pre_solve(parameters)
  759. var = self.free_symbols
  760. k = -int(self.coeff[1])
  761. n = self.dimension
  762. result = DiophantineSolutionSet(var, parameters=self.parameters)
  763. if k < 0 or limit < 1:
  764. return result
  765. signs = [-1 if x.is_nonpositive else 1 for x in var]
  766. negs = signs.count(-1) != 0
  767. took = 0
  768. for t in sum_of_squares(k, n, zeros=True):
  769. if negs:
  770. result.add([signs[i]*j for i, j in enumerate(t)])
  771. else:
  772. result.add(t)
  773. took += 1
  774. if took == limit:
  775. break
  776. return result
  777. class GeneralPythagorean(DiophantineEquationType):
  778. """
  779. Representation of the general pythagorean equation,
  780. `a_{1}^2x_{1}^2 + a_{2}^2x_{2}^2 + . . . + a_{n}^2x_{n}^2 - a_{n + 1}^2x_{n + 1}^2 = 0`.
  781. Examples
  782. ========
  783. >>> from sympy.solvers.diophantine.diophantine import GeneralPythagorean
  784. >>> from sympy.abc import a, b, c, d, e, x, y, z, t
  785. >>> GeneralPythagorean(a**2 + b**2 + c**2 - d**2).solve()
  786. {(t_0**2 + t_1**2 - t_2**2, 2*t_0*t_2, 2*t_1*t_2, t_0**2 + t_1**2 + t_2**2)}
  787. >>> GeneralPythagorean(9*a**2 - 4*b**2 + 16*c**2 + 25*d**2 + e**2).solve(parameters=[x, y, z, t])
  788. {(-10*t**2 + 10*x**2 + 10*y**2 + 10*z**2, 15*t**2 + 15*x**2 + 15*y**2 + 15*z**2, 15*t*x, 12*t*y, 60*t*z)}
  789. """
  790. name = 'general_pythagorean'
  791. def matches(self):
  792. if not (self.total_degree == 2 and self.dimension >= 3):
  793. return False
  794. if not self.homogeneous_order:
  795. return False
  796. if any(k.is_Mul for k in self.coeff):
  797. return False
  798. if all(self.coeff[k] == 1 for k in self.coeff if k != 1):
  799. return False
  800. if not all(is_square(abs(self.coeff[k])) for k in self.coeff):
  801. return False
  802. # all but one has the same sign
  803. # e.g. 4*x**2 + y**2 - 4*z**2
  804. return abs(sum(sign(self.coeff[k]) for k in self.coeff)) == self.dimension - 2
  805. @property
  806. def n_parameters(self):
  807. return self.dimension - 1
  808. def solve(self, parameters=None, limit=1):
  809. self.pre_solve(parameters)
  810. coeff = self.coeff
  811. var = self.free_symbols
  812. n = self.dimension
  813. if sign(coeff[var[0] ** 2]) + sign(coeff[var[1] ** 2]) + sign(coeff[var[2] ** 2]) < 0:
  814. for key in coeff.keys():
  815. coeff[key] = -coeff[key]
  816. result = DiophantineSolutionSet(var, parameters=self.parameters)
  817. index = 0
  818. for i, v in enumerate(var):
  819. if sign(coeff[v ** 2]) == -1:
  820. index = i
  821. m = result.parameters
  822. ith = sum(m_i ** 2 for m_i in m)
  823. L = [ith - 2 * m[n - 2] ** 2]
  824. L.extend([2 * m[i] * m[n - 2] for i in range(n - 2)])
  825. sol = L[:index] + [ith] + L[index:]
  826. lcm = 1
  827. for i, v in enumerate(var):
  828. if i == index or (index > 0 and i == 0) or (index == 0 and i == 1):
  829. lcm = ilcm(lcm, sqrt(abs(coeff[v ** 2])))
  830. else:
  831. s = sqrt(coeff[v ** 2])
  832. lcm = ilcm(lcm, s if _odd(s) else s // 2)
  833. for i, v in enumerate(var):
  834. sol[i] = (lcm * sol[i]) / sqrt(abs(coeff[v ** 2]))
  835. result.add(sol)
  836. return result
  837. class CubicThue(DiophantineEquationType):
  838. """
  839. Representation of a cubic Thue diophantine equation.
  840. A cubic Thue diophantine equation is a polynomial of the form
  841. `f(x, y) = r` of degree 3, where `x` and `y` are integers
  842. and `r` is a rational number.
  843. No solver is currently implemented for this equation type.
  844. Examples
  845. ========
  846. >>> from sympy.abc import x, y
  847. >>> from sympy.solvers.diophantine.diophantine import CubicThue
  848. >>> c1 = CubicThue(x**3 + y**2 + 1)
  849. >>> c1.matches()
  850. True
  851. """
  852. name = 'cubic_thue'
  853. def matches(self):
  854. return self.total_degree == 3 and self.dimension == 2
  855. class GeneralSumOfEvenPowers(DiophantineEquationType):
  856. """
  857. Representation of the diophantine equation
  858. `x_{1}^e + x_{2}^e + . . . + x_{n}^e - k = 0`
  859. where `e` is an even, integer power.
  860. Examples
  861. ========
  862. >>> from sympy.solvers.diophantine.diophantine import GeneralSumOfEvenPowers
  863. >>> from sympy.abc import a, b
  864. >>> GeneralSumOfEvenPowers(a**4 + b**4 - (2**4 + 3**4)).solve()
  865. {(2, 3)}
  866. """
  867. name = 'general_sum_of_even_powers'
  868. def matches(self):
  869. if not self.total_degree > 3:
  870. return False
  871. if self.total_degree % 2 != 0:
  872. return False
  873. if not all(k.is_Pow and k.exp == self.total_degree for k in self.coeff if k != 1):
  874. return False
  875. return all(self.coeff[k] == 1 for k in self.coeff if k != 1)
  876. def solve(self, parameters=None, limit=1):
  877. self.pre_solve(parameters)
  878. var = self.free_symbols
  879. coeff = self.coeff
  880. p = None
  881. for q in coeff.keys():
  882. if q.is_Pow and coeff[q]:
  883. p = q.exp
  884. k = len(var)
  885. n = -coeff[1]
  886. result = DiophantineSolutionSet(var, parameters=self.parameters)
  887. if n < 0 or limit < 1:
  888. return result
  889. sign = [-1 if x.is_nonpositive else 1 for x in var]
  890. negs = sign.count(-1) != 0
  891. took = 0
  892. for t in power_representation(n, p, k):
  893. if negs:
  894. result.add([sign[i]*j for i, j in enumerate(t)])
  895. else:
  896. result.add(t)
  897. took += 1
  898. if took == limit:
  899. break
  900. return result
  901. # these types are known (but not necessarily handled)
  902. # note that order is important here (in the current solver state)
  903. all_diop_classes = [
  904. Linear,
  905. Univariate,
  906. BinaryQuadratic,
  907. InhomogeneousTernaryQuadratic,
  908. HomogeneousTernaryQuadraticNormal,
  909. HomogeneousTernaryQuadratic,
  910. InhomogeneousGeneralQuadratic,
  911. HomogeneousGeneralQuadratic,
  912. GeneralSumOfSquares,
  913. GeneralPythagorean,
  914. CubicThue,
  915. GeneralSumOfEvenPowers,
  916. ]
  917. diop_known = {diop_class.name for diop_class in all_diop_classes}
  918. def _is_int(i):
  919. try:
  920. as_int(i)
  921. return True
  922. except ValueError:
  923. pass
  924. def _sorted_tuple(*i):
  925. return tuple(sorted(i))
  926. def _remove_gcd(*x):
  927. try:
  928. g = igcd(*x)
  929. except ValueError:
  930. fx = list(filter(None, x))
  931. if len(fx) < 2:
  932. return x
  933. g = igcd(*[i.as_content_primitive()[0] for i in fx])
  934. except TypeError:
  935. raise TypeError('_remove_gcd(a,b,c) or _remove_gcd(*container)')
  936. if g == 1:
  937. return x
  938. return tuple([i//g for i in x])
  939. def _rational_pq(a, b):
  940. # return `(numer, denom)` for a/b; sign in numer and gcd removed
  941. return _remove_gcd(sign(b)*a, abs(b))
  942. def _nint_or_floor(p, q):
  943. # return nearest int to p/q; in case of tie return floor(p/q)
  944. w, r = divmod(p, q)
  945. if abs(r) <= abs(q)//2:
  946. return w
  947. return w + 1
  948. def _odd(i):
  949. return i % 2 != 0
  950. def _even(i):
  951. return i % 2 == 0
  952. def diophantine(eq, param=symbols("t", integer=True), syms=None,
  953. permute=False):
  954. """
  955. Simplify the solution procedure of diophantine equation ``eq`` by
  956. converting it into a product of terms which should equal zero.
  957. Explanation
  958. ===========
  959. For example, when solving, `x^2 - y^2 = 0` this is treated as
  960. `(x + y)(x - y) = 0` and `x + y = 0` and `x - y = 0` are solved
  961. independently and combined. Each term is solved by calling
  962. ``diop_solve()``. (Although it is possible to call ``diop_solve()``
  963. directly, one must be careful to pass an equation in the correct
  964. form and to interpret the output correctly; ``diophantine()`` is
  965. the public-facing function to use in general.)
  966. Output of ``diophantine()`` is a set of tuples. The elements of the
  967. tuple are the solutions for each variable in the equation and
  968. are arranged according to the alphabetic ordering of the variables.
  969. e.g. For an equation with two variables, `a` and `b`, the first
  970. element of the tuple is the solution for `a` and the second for `b`.
  971. Usage
  972. =====
  973. ``diophantine(eq, t, syms)``: Solve the diophantine
  974. equation ``eq``.
  975. ``t`` is the optional parameter to be used by ``diop_solve()``.
  976. ``syms`` is an optional list of symbols which determines the
  977. order of the elements in the returned tuple.
  978. By default, only the base solution is returned. If ``permute`` is set to
  979. True then permutations of the base solution and/or permutations of the
  980. signs of the values will be returned when applicable.
  981. Details
  982. =======
  983. ``eq`` should be an expression which is assumed to be zero.
  984. ``t`` is the parameter to be used in the solution.
  985. Examples
  986. ========
  987. >>> from sympy import diophantine
  988. >>> from sympy.abc import a, b
  989. >>> eq = a**4 + b**4 - (2**4 + 3**4)
  990. >>> diophantine(eq)
  991. {(2, 3)}
  992. >>> diophantine(eq, permute=True)
  993. {(-3, -2), (-3, 2), (-2, -3), (-2, 3), (2, -3), (2, 3), (3, -2), (3, 2)}
  994. >>> from sympy.abc import x, y, z
  995. >>> diophantine(x**2 - y**2)
  996. {(t_0, -t_0), (t_0, t_0)}
  997. >>> diophantine(x*(2*x + 3*y - z))
  998. {(0, n1, n2), (t_0, t_1, 2*t_0 + 3*t_1)}
  999. >>> diophantine(x**2 + 3*x*y + 4*x)
  1000. {(0, n1), (3*t_0 - 4, -t_0)}
  1001. See Also
  1002. ========
  1003. diop_solve
  1004. sympy.utilities.iterables.permute_signs
  1005. sympy.utilities.iterables.signed_permutations
  1006. """
  1007. eq = _sympify(eq)
  1008. if isinstance(eq, Eq):
  1009. eq = eq.lhs - eq.rhs
  1010. try:
  1011. var = list(eq.expand(force=True).free_symbols)
  1012. var.sort(key=default_sort_key)
  1013. if syms:
  1014. if not is_sequence(syms):
  1015. raise TypeError(
  1016. 'syms should be given as a sequence, e.g. a list')
  1017. syms = [i for i in syms if i in var]
  1018. if syms != var:
  1019. dict_sym_index = dict(zip(syms, range(len(syms))))
  1020. return {tuple([t[dict_sym_index[i]] for i in var])
  1021. for t in diophantine(eq, param, permute=permute)}
  1022. n, d = eq.as_numer_denom()
  1023. if n.is_number:
  1024. return set()
  1025. if not d.is_number:
  1026. dsol = diophantine(d)
  1027. good = diophantine(n) - dsol
  1028. return {s for s in good if _mexpand(d.subs(zip(var, s)))}
  1029. else:
  1030. eq = n
  1031. eq = factor_terms(eq)
  1032. assert not eq.is_number
  1033. eq = eq.as_independent(*var, as_Add=False)[1]
  1034. p = Poly(eq)
  1035. assert not any(g.is_number for g in p.gens)
  1036. eq = p.as_expr()
  1037. assert eq.is_polynomial()
  1038. except (GeneratorsNeeded, AssertionError):
  1039. raise TypeError(filldedent('''
  1040. Equation should be a polynomial with Rational coefficients.'''))
  1041. # permute only sign
  1042. do_permute_signs = False
  1043. # permute sign and values
  1044. do_permute_signs_var = False
  1045. # permute few signs
  1046. permute_few_signs = False
  1047. try:
  1048. # if we know that factoring should not be attempted, skip
  1049. # the factoring step
  1050. v, c, t = classify_diop(eq)
  1051. # check for permute sign
  1052. if permute:
  1053. len_var = len(v)
  1054. permute_signs_for = [
  1055. GeneralSumOfSquares.name,
  1056. GeneralSumOfEvenPowers.name]
  1057. permute_signs_check = [
  1058. HomogeneousTernaryQuadratic.name,
  1059. HomogeneousTernaryQuadraticNormal.name,
  1060. BinaryQuadratic.name]
  1061. if t in permute_signs_for:
  1062. do_permute_signs_var = True
  1063. elif t in permute_signs_check:
  1064. # if all the variables in eq have even powers
  1065. # then do_permute_sign = True
  1066. if len_var == 3:
  1067. var_mul = list(subsets(v, 2))
  1068. # here var_mul is like [(x, y), (x, z), (y, z)]
  1069. xy_coeff = True
  1070. x_coeff = True
  1071. var1_mul_var2 = (a[0]*a[1] for a in var_mul)
  1072. # if coeff(y*z), coeff(y*x), coeff(x*z) is not 0 then
  1073. # `xy_coeff` => True and do_permute_sign => False.
  1074. # Means no permuted solution.
  1075. for v1_mul_v2 in var1_mul_var2:
  1076. try:
  1077. coeff = c[v1_mul_v2]
  1078. except KeyError:
  1079. coeff = 0
  1080. xy_coeff = bool(xy_coeff) and bool(coeff)
  1081. var_mul = list(subsets(v, 1))
  1082. # here var_mul is like [(x,), (y, )]
  1083. for v1 in var_mul:
  1084. try:
  1085. coeff = c[v1[0]]
  1086. except KeyError:
  1087. coeff = 0
  1088. x_coeff = bool(x_coeff) and bool(coeff)
  1089. if not any((xy_coeff, x_coeff)):
  1090. # means only x**2, y**2, z**2, const is present
  1091. do_permute_signs = True
  1092. elif not x_coeff:
  1093. permute_few_signs = True
  1094. elif len_var == 2:
  1095. var_mul = list(subsets(v, 2))
  1096. # here var_mul is like [(x, y)]
  1097. xy_coeff = True
  1098. x_coeff = True
  1099. var1_mul_var2 = (x[0]*x[1] for x in var_mul)
  1100. for v1_mul_v2 in var1_mul_var2:
  1101. try:
  1102. coeff = c[v1_mul_v2]
  1103. except KeyError:
  1104. coeff = 0
  1105. xy_coeff = bool(xy_coeff) and bool(coeff)
  1106. var_mul = list(subsets(v, 1))
  1107. # here var_mul is like [(x,), (y, )]
  1108. for v1 in var_mul:
  1109. try:
  1110. coeff = c[v1[0]]
  1111. except KeyError:
  1112. coeff = 0
  1113. x_coeff = bool(x_coeff) and bool(coeff)
  1114. if not any((xy_coeff, x_coeff)):
  1115. # means only x**2, y**2 and const is present
  1116. # so we can get more soln by permuting this soln.
  1117. do_permute_signs = True
  1118. elif not x_coeff:
  1119. # when coeff(x), coeff(y) is not present then signs of
  1120. # x, y can be permuted such that their sign are same
  1121. # as sign of x*y.
  1122. # e.g 1. (x_val,y_val)=> (x_val,y_val), (-x_val,-y_val)
  1123. # 2. (-x_vall, y_val)=> (-x_val,y_val), (x_val,-y_val)
  1124. permute_few_signs = True
  1125. if t == 'general_sum_of_squares':
  1126. # trying to factor such expressions will sometimes hang
  1127. terms = [(eq, 1)]
  1128. else:
  1129. raise TypeError
  1130. except (TypeError, NotImplementedError):
  1131. fl = factor_list(eq)
  1132. if fl[0].is_Rational and fl[0] != 1:
  1133. return diophantine(eq/fl[0], param=param, syms=syms, permute=permute)
  1134. terms = fl[1]
  1135. sols = set()
  1136. for term in terms:
  1137. base, _ = term
  1138. var_t, _, eq_type = classify_diop(base, _dict=False)
  1139. _, base = signsimp(base, evaluate=False).as_coeff_Mul()
  1140. solution = diop_solve(base, param)
  1141. if eq_type in [
  1142. Linear.name,
  1143. HomogeneousTernaryQuadratic.name,
  1144. HomogeneousTernaryQuadraticNormal.name,
  1145. GeneralPythagorean.name]:
  1146. sols.add(merge_solution(var, var_t, solution))
  1147. elif eq_type in [
  1148. BinaryQuadratic.name,
  1149. GeneralSumOfSquares.name,
  1150. GeneralSumOfEvenPowers.name,
  1151. Univariate.name]:
  1152. for sol in solution:
  1153. sols.add(merge_solution(var, var_t, sol))
  1154. else:
  1155. raise NotImplementedError('unhandled type: %s' % eq_type)
  1156. # remove null merge results
  1157. if () in sols:
  1158. sols.remove(())
  1159. null = tuple([0]*len(var))
  1160. # if there is no solution, return trivial solution
  1161. if not sols and eq.subs(zip(var, null)).is_zero:
  1162. sols.add(null)
  1163. final_soln = set()
  1164. for sol in sols:
  1165. if all(_is_int(s) for s in sol):
  1166. if do_permute_signs:
  1167. permuted_sign = set(permute_signs(sol))
  1168. final_soln.update(permuted_sign)
  1169. elif permute_few_signs:
  1170. lst = list(permute_signs(sol))
  1171. lst = list(filter(lambda x: x[0]*x[1] == sol[1]*sol[0], lst))
  1172. permuted_sign = set(lst)
  1173. final_soln.update(permuted_sign)
  1174. elif do_permute_signs_var:
  1175. permuted_sign_var = set(signed_permutations(sol))
  1176. final_soln.update(permuted_sign_var)
  1177. else:
  1178. final_soln.add(sol)
  1179. else:
  1180. final_soln.add(sol)
  1181. return final_soln
  1182. def merge_solution(var, var_t, solution):
  1183. """
  1184. This is used to construct the full solution from the solutions of sub
  1185. equations.
  1186. Explanation
  1187. ===========
  1188. For example when solving the equation `(x - y)(x^2 + y^2 - z^2) = 0`,
  1189. solutions for each of the equations `x - y = 0` and `x^2 + y^2 - z^2` are
  1190. found independently. Solutions for `x - y = 0` are `(x, y) = (t, t)`. But
  1191. we should introduce a value for z when we output the solution for the
  1192. original equation. This function converts `(t, t)` into `(t, t, n_{1})`
  1193. where `n_{1}` is an integer parameter.
  1194. """
  1195. sol = []
  1196. if None in solution:
  1197. return ()
  1198. solution = iter(solution)
  1199. params = numbered_symbols("n", integer=True, start=1)
  1200. for v in var:
  1201. if v in var_t:
  1202. sol.append(next(solution))
  1203. else:
  1204. sol.append(next(params))
  1205. for val, symb in zip(sol, var):
  1206. if check_assumptions(val, **symb.assumptions0) is False:
  1207. return ()
  1208. return tuple(sol)
  1209. def _diop_solve(eq, params=None):
  1210. for diop_type in all_diop_classes:
  1211. if diop_type(eq).matches():
  1212. return diop_type(eq).solve(parameters=params)
  1213. def diop_solve(eq, param=symbols("t", integer=True)):
  1214. """
  1215. Solves the diophantine equation ``eq``.
  1216. Explanation
  1217. ===========
  1218. Unlike ``diophantine()``, factoring of ``eq`` is not attempted. Uses
  1219. ``classify_diop()`` to determine the type of the equation and calls
  1220. the appropriate solver function.
  1221. Use of ``diophantine()`` is recommended over other helper functions.
  1222. ``diop_solve()`` can return either a set or a tuple depending on the
  1223. nature of the equation.
  1224. Usage
  1225. =====
  1226. ``diop_solve(eq, t)``: Solve diophantine equation, ``eq`` using ``t``
  1227. as a parameter if needed.
  1228. Details
  1229. =======
  1230. ``eq`` should be an expression which is assumed to be zero.
  1231. ``t`` is a parameter to be used in the solution.
  1232. Examples
  1233. ========
  1234. >>> from sympy.solvers.diophantine import diop_solve
  1235. >>> from sympy.abc import x, y, z, w
  1236. >>> diop_solve(2*x + 3*y - 5)
  1237. (3*t_0 - 5, 5 - 2*t_0)
  1238. >>> diop_solve(4*x + 3*y - 4*z + 5)
  1239. (t_0, 8*t_0 + 4*t_1 + 5, 7*t_0 + 3*t_1 + 5)
  1240. >>> diop_solve(x + 3*y - 4*z + w - 6)
  1241. (t_0, t_0 + t_1, 6*t_0 + 5*t_1 + 4*t_2 - 6, 5*t_0 + 4*t_1 + 3*t_2 - 6)
  1242. >>> diop_solve(x**2 + y**2 - 5)
  1243. {(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)}
  1244. See Also
  1245. ========
  1246. diophantine()
  1247. """
  1248. var, coeff, eq_type = classify_diop(eq, _dict=False)
  1249. if eq_type == Linear.name:
  1250. return diop_linear(eq, param)
  1251. elif eq_type == BinaryQuadratic.name:
  1252. return diop_quadratic(eq, param)
  1253. elif eq_type == HomogeneousTernaryQuadratic.name:
  1254. return diop_ternary_quadratic(eq, parameterize=True)
  1255. elif eq_type == HomogeneousTernaryQuadraticNormal.name:
  1256. return diop_ternary_quadratic_normal(eq, parameterize=True)
  1257. elif eq_type == GeneralPythagorean.name:
  1258. return diop_general_pythagorean(eq, param)
  1259. elif eq_type == Univariate.name:
  1260. return diop_univariate(eq)
  1261. elif eq_type == GeneralSumOfSquares.name:
  1262. return diop_general_sum_of_squares(eq, limit=S.Infinity)
  1263. elif eq_type == GeneralSumOfEvenPowers.name:
  1264. return diop_general_sum_of_even_powers(eq, limit=S.Infinity)
  1265. if eq_type is not None and eq_type not in diop_known:
  1266. raise ValueError(filldedent('''
  1267. Although this type of equation was identified, it is not yet
  1268. handled. It should, however, be listed in `diop_known` at the
  1269. top of this file. Developers should see comments at the end of
  1270. `classify_diop`.
  1271. ''')) # pragma: no cover
  1272. else:
  1273. raise NotImplementedError(
  1274. 'No solver has been written for %s.' % eq_type)
  1275. def classify_diop(eq, _dict=True):
  1276. # docstring supplied externally
  1277. matched = False
  1278. diop_type = None
  1279. for diop_class in all_diop_classes:
  1280. diop_type = diop_class(eq)
  1281. if diop_type.matches():
  1282. matched = True
  1283. break
  1284. if matched:
  1285. return diop_type.free_symbols, dict(diop_type.coeff) if _dict else diop_type.coeff, diop_type.name
  1286. # new diop type instructions
  1287. # --------------------------
  1288. # if this error raises and the equation *can* be classified,
  1289. # * it should be identified in the if-block above
  1290. # * the type should be added to the diop_known
  1291. # if a solver can be written for it,
  1292. # * a dedicated handler should be written (e.g. diop_linear)
  1293. # * it should be passed to that handler in diop_solve
  1294. raise NotImplementedError(filldedent('''
  1295. This equation is not yet recognized or else has not been
  1296. simplified sufficiently to put it in a form recognized by
  1297. diop_classify().'''))
  1298. classify_diop.func_doc = ( # type: ignore
  1299. '''
  1300. Helper routine used by diop_solve() to find information about ``eq``.
  1301. Explanation
  1302. ===========
  1303. Returns a tuple containing the type of the diophantine equation
  1304. along with the variables (free symbols) and their coefficients.
  1305. Variables are returned as a list and coefficients are returned
  1306. as a dict with the key being the respective term and the constant
  1307. term is keyed to 1. The type is one of the following:
  1308. * %s
  1309. Usage
  1310. =====
  1311. ``classify_diop(eq)``: Return variables, coefficients and type of the
  1312. ``eq``.
  1313. Details
  1314. =======
  1315. ``eq`` should be an expression which is assumed to be zero.
  1316. ``_dict`` is for internal use: when True (default) a dict is returned,
  1317. otherwise a defaultdict which supplies 0 for missing keys is returned.
  1318. Examples
  1319. ========
  1320. >>> from sympy.solvers.diophantine import classify_diop
  1321. >>> from sympy.abc import x, y, z, w, t
  1322. >>> classify_diop(4*x + 6*y - 4)
  1323. ([x, y], {1: -4, x: 4, y: 6}, 'linear')
  1324. >>> classify_diop(x + 3*y -4*z + 5)
  1325. ([x, y, z], {1: 5, x: 1, y: 3, z: -4}, 'linear')
  1326. >>> classify_diop(x**2 + y**2 - x*y + x + 5)
  1327. ([x, y], {1: 5, x: 1, x**2: 1, y**2: 1, x*y: -1}, 'binary_quadratic')
  1328. ''' % ('\n * '.join(sorted(diop_known))))
  1329. def diop_linear(eq, param=symbols("t", integer=True)):
  1330. """
  1331. Solves linear diophantine equations.
  1332. A linear diophantine equation is an equation of the form `a_{1}x_{1} +
  1333. a_{2}x_{2} + .. + a_{n}x_{n} = 0` where `a_{1}, a_{2}, ..a_{n}` are
  1334. integer constants and `x_{1}, x_{2}, ..x_{n}` are integer variables.
  1335. Usage
  1336. =====
  1337. ``diop_linear(eq)``: Returns a tuple containing solutions to the
  1338. diophantine equation ``eq``. Values in the tuple is arranged in the same
  1339. order as the sorted variables.
  1340. Details
  1341. =======
  1342. ``eq`` is a linear diophantine equation which is assumed to be zero.
  1343. ``param`` is the parameter to be used in the solution.
  1344. Examples
  1345. ========
  1346. >>> from sympy.solvers.diophantine.diophantine import diop_linear
  1347. >>> from sympy.abc import x, y, z
  1348. >>> diop_linear(2*x - 3*y - 5) # solves equation 2*x - 3*y - 5 == 0
  1349. (3*t_0 - 5, 2*t_0 - 5)
  1350. Here x = -3*t_0 - 5 and y = -2*t_0 - 5
  1351. >>> diop_linear(2*x - 3*y - 4*z -3)
  1352. (t_0, 2*t_0 + 4*t_1 + 3, -t_0 - 3*t_1 - 3)
  1353. See Also
  1354. ========
  1355. diop_quadratic(), diop_ternary_quadratic(), diop_general_pythagorean(),
  1356. diop_general_sum_of_squares()
  1357. """
  1358. var, coeff, diop_type = classify_diop(eq, _dict=False)
  1359. if diop_type == Linear.name:
  1360. parameters = None
  1361. if param is not None:
  1362. parameters = symbols('%s_0:%i' % (param, len(var)), integer=True)
  1363. result = Linear(eq).solve(parameters=parameters)
  1364. if param is None:
  1365. result = result(*[0]*len(result.parameters))
  1366. if len(result) > 0:
  1367. return list(result)[0]
  1368. else:
  1369. return tuple([None]*len(result.parameters))
  1370. def base_solution_linear(c, a, b, t=None):
  1371. """
  1372. Return the base solution for the linear equation, `ax + by = c`.
  1373. Explanation
  1374. ===========
  1375. Used by ``diop_linear()`` to find the base solution of a linear
  1376. Diophantine equation. If ``t`` is given then the parametrized solution is
  1377. returned.
  1378. Usage
  1379. =====
  1380. ``base_solution_linear(c, a, b, t)``: ``a``, ``b``, ``c`` are coefficients
  1381. in `ax + by = c` and ``t`` is the parameter to be used in the solution.
  1382. Examples
  1383. ========
  1384. >>> from sympy.solvers.diophantine.diophantine import base_solution_linear
  1385. >>> from sympy.abc import t
  1386. >>> base_solution_linear(5, 2, 3) # equation 2*x + 3*y = 5
  1387. (-5, 5)
  1388. >>> base_solution_linear(0, 5, 7) # equation 5*x + 7*y = 0
  1389. (0, 0)
  1390. >>> base_solution_linear(5, 2, 3, t) # equation 2*x + 3*y = 5
  1391. (3*t - 5, 5 - 2*t)
  1392. >>> base_solution_linear(0, 5, 7, t) # equation 5*x + 7*y = 0
  1393. (7*t, -5*t)
  1394. """
  1395. a, b, c = _remove_gcd(a, b, c)
  1396. if c == 0:
  1397. if t is not None:
  1398. if b < 0:
  1399. t = -t
  1400. return (b*t, -a*t)
  1401. else:
  1402. return (0, 0)
  1403. else:
  1404. x0, y0, d = igcdex(abs(a), abs(b))
  1405. x0 *= sign(a)
  1406. y0 *= sign(b)
  1407. if divisible(c, d):
  1408. if t is not None:
  1409. if b < 0:
  1410. t = -t
  1411. return (c*x0 + b*t, c*y0 - a*t)
  1412. else:
  1413. return (c*x0, c*y0)
  1414. else:
  1415. return (None, None)
  1416. def diop_univariate(eq):
  1417. """
  1418. Solves a univariate diophantine equations.
  1419. Explanation
  1420. ===========
  1421. A univariate diophantine equation is an equation of the form
  1422. `a_{0} + a_{1}x + a_{2}x^2 + .. + a_{n}x^n = 0` where `a_{1}, a_{2}, ..a_{n}` are
  1423. integer constants and `x` is an integer variable.
  1424. Usage
  1425. =====
  1426. ``diop_univariate(eq)``: Returns a set containing solutions to the
  1427. diophantine equation ``eq``.
  1428. Details
  1429. =======
  1430. ``eq`` is a univariate diophantine equation which is assumed to be zero.
  1431. Examples
  1432. ========
  1433. >>> from sympy.solvers.diophantine.diophantine import diop_univariate
  1434. >>> from sympy.abc import x
  1435. >>> diop_univariate((x - 2)*(x - 3)**2) # solves equation (x - 2)*(x - 3)**2 == 0
  1436. {(2,), (3,)}
  1437. """
  1438. var, coeff, diop_type = classify_diop(eq, _dict=False)
  1439. if diop_type == Univariate.name:
  1440. return {(int(i),) for i in solveset_real(
  1441. eq, var[0]).intersect(S.Integers)}
  1442. def divisible(a, b):
  1443. """
  1444. Returns `True` if ``a`` is divisible by ``b`` and `False` otherwise.
  1445. """
  1446. return not a % b
  1447. def diop_quadratic(eq, param=symbols("t", integer=True)):
  1448. """
  1449. Solves quadratic diophantine equations.
  1450. i.e. equations of the form `Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0`. Returns a
  1451. set containing the tuples `(x, y)` which contains the solutions. If there
  1452. are no solutions then `(None, None)` is returned.
  1453. Usage
  1454. =====
  1455. ``diop_quadratic(eq, param)``: ``eq`` is a quadratic binary diophantine
  1456. equation. ``param`` is used to indicate the parameter to be used in the
  1457. solution.
  1458. Details
  1459. =======
  1460. ``eq`` should be an expression which is assumed to be zero.
  1461. ``param`` is a parameter to be used in the solution.
  1462. Examples
  1463. ========
  1464. >>> from sympy.abc import x, y, t
  1465. >>> from sympy.solvers.diophantine.diophantine import diop_quadratic
  1466. >>> diop_quadratic(x**2 + y**2 + 2*x + 2*y + 2, t)
  1467. {(-1, -1)}
  1468. References
  1469. ==========
  1470. .. [1] Methods to solve Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0, [online],
  1471. Available: https://www.alpertron.com.ar/METHODS.HTM
  1472. .. [2] Solving the equation ax^2+ bxy + cy^2 + dx + ey + f= 0, [online],
  1473. Available: https://web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
  1474. See Also
  1475. ========
  1476. diop_linear(), diop_ternary_quadratic(), diop_general_sum_of_squares(),
  1477. diop_general_pythagorean()
  1478. """
  1479. var, coeff, diop_type = classify_diop(eq, _dict=False)
  1480. if diop_type == BinaryQuadratic.name:
  1481. if param is not None:
  1482. parameters = [param, Symbol("u", integer=True)]
  1483. else:
  1484. parameters = None
  1485. return set(BinaryQuadratic(eq).solve(parameters=parameters))
  1486. def is_solution_quad(var, coeff, u, v):
  1487. """
  1488. Check whether `(u, v)` is solution to the quadratic binary diophantine
  1489. equation with the variable list ``var`` and coefficient dictionary
  1490. ``coeff``.
  1491. Not intended for use by normal users.
  1492. """
  1493. reps = dict(zip(var, (u, v)))
  1494. eq = Add(*[j*i.xreplace(reps) for i, j in coeff.items()])
  1495. return _mexpand(eq) == 0
  1496. def diop_DN(D, N, t=symbols("t", integer=True)):
  1497. """
  1498. Solves the equation `x^2 - Dy^2 = N`.
  1499. Explanation
  1500. ===========
  1501. Mainly concerned with the case `D > 0, D` is not a perfect square,
  1502. which is the same as the generalized Pell equation. The LMM
  1503. algorithm [1]_ is used to solve this equation.
  1504. Returns one solution tuple, (`x, y)` for each class of the solutions.
  1505. Other solutions of the class can be constructed according to the
  1506. values of ``D`` and ``N``.
  1507. Usage
  1508. =====
  1509. ``diop_DN(D, N, t)``: D and N are integers as in `x^2 - Dy^2 = N` and
  1510. ``t`` is the parameter to be used in the solutions.
  1511. Details
  1512. =======
  1513. ``D`` and ``N`` correspond to D and N in the equation.
  1514. ``t`` is the parameter to be used in the solutions.
  1515. Examples
  1516. ========
  1517. >>> from sympy.solvers.diophantine.diophantine import diop_DN
  1518. >>> diop_DN(13, -4) # Solves equation x**2 - 13*y**2 = -4
  1519. [(3, 1), (393, 109), (36, 10)]
  1520. The output can be interpreted as follows: There are three fundamental
  1521. solutions to the equation `x^2 - 13y^2 = -4` given by (3, 1), (393, 109)
  1522. and (36, 10). Each tuple is in the form (x, y), i.e. solution (3, 1) means
  1523. that `x = 3` and `y = 1`.
  1524. >>> diop_DN(986, 1) # Solves equation x**2 - 986*y**2 = 1
  1525. [(49299, 1570)]
  1526. See Also
  1527. ========
  1528. find_DN(), diop_bf_DN()
  1529. References
  1530. ==========
  1531. .. [1] Solving the generalized Pell equation x**2 - D*y**2 = N, John P.
  1532. Robertson, July 31, 2004, Pages 16 - 17. [online], Available:
  1533. https://web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
  1534. """
  1535. if D < 0:
  1536. if N == 0:
  1537. return [(0, 0)]
  1538. elif N < 0:
  1539. return []
  1540. elif N > 0:
  1541. sol = []
  1542. for d in divisors(square_factor(N)):
  1543. sols = cornacchia(1, -D, N // d**2)
  1544. if sols:
  1545. for x, y in sols:
  1546. sol.append((d*x, d*y))
  1547. if D == -1:
  1548. sol.append((d*y, d*x))
  1549. return sol
  1550. elif D == 0:
  1551. if N < 0:
  1552. return []
  1553. if N == 0:
  1554. return [(0, t)]
  1555. sN, _exact = integer_nthroot(N, 2)
  1556. if _exact:
  1557. return [(sN, t)]
  1558. else:
  1559. return []
  1560. else: # D > 0
  1561. sD, _exact = integer_nthroot(D, 2)
  1562. if _exact:
  1563. if N == 0:
  1564. return [(sD*t, t)]
  1565. else:
  1566. sol = []
  1567. for y in range(floor(sign(N)*(N - 1)/(2*sD)) + 1):
  1568. try:
  1569. sq, _exact = integer_nthroot(D*y**2 + N, 2)
  1570. except ValueError:
  1571. _exact = False
  1572. if _exact:
  1573. sol.append((sq, y))
  1574. return sol
  1575. elif 1 < N**2 < D:
  1576. # It is much faster to call `_special_diop_DN`.
  1577. return _special_diop_DN(D, N)
  1578. else:
  1579. if N == 0:
  1580. return [(0, 0)]
  1581. elif abs(N) == 1:
  1582. pqa = PQa(0, 1, D)
  1583. j = 0
  1584. G = []
  1585. B = []
  1586. for i in pqa:
  1587. a = i[2]
  1588. G.append(i[5])
  1589. B.append(i[4])
  1590. if j != 0 and a == 2*sD:
  1591. break
  1592. j = j + 1
  1593. if _odd(j):
  1594. if N == -1:
  1595. x = G[j - 1]
  1596. y = B[j - 1]
  1597. else:
  1598. count = j
  1599. while count < 2*j - 1:
  1600. i = next(pqa)
  1601. G.append(i[5])
  1602. B.append(i[4])
  1603. count += 1
  1604. x = G[count]
  1605. y = B[count]
  1606. else:
  1607. if N == 1:
  1608. x = G[j - 1]
  1609. y = B[j - 1]
  1610. else:
  1611. return []
  1612. return [(x, y)]
  1613. else:
  1614. fs = []
  1615. sol = []
  1616. div = divisors(N)
  1617. for d in div:
  1618. if divisible(N, d**2):
  1619. fs.append(d)
  1620. for f in fs:
  1621. m = N // f**2
  1622. zs = sqrt_mod(D, abs(m), all_roots=True)
  1623. zs = [i for i in zs if i <= abs(m) // 2 ]
  1624. if abs(m) != 2:
  1625. zs = zs + [-i for i in zs if i] # omit dupl 0
  1626. for z in zs:
  1627. pqa = PQa(z, abs(m), D)
  1628. j = 0
  1629. G = []
  1630. B = []
  1631. for i in pqa:
  1632. G.append(i[5])
  1633. B.append(i[4])
  1634. if j != 0 and abs(i[1]) == 1:
  1635. r = G[j-1]
  1636. s = B[j-1]
  1637. if r**2 - D*s**2 == m:
  1638. sol.append((f*r, f*s))
  1639. elif diop_DN(D, -1) != []:
  1640. a = diop_DN(D, -1)
  1641. sol.append((f*(r*a[0][0] + a[0][1]*s*D), f*(r*a[0][1] + s*a[0][0])))
  1642. break
  1643. j = j + 1
  1644. if j == length(z, abs(m), D):
  1645. break
  1646. return sol
  1647. def _special_diop_DN(D, N):
  1648. """
  1649. Solves the equation `x^2 - Dy^2 = N` for the special case where
  1650. `1 < N**2 < D` and `D` is not a perfect square.
  1651. It is better to call `diop_DN` rather than this function, as
  1652. the former checks the condition `1 < N**2 < D`, and calls the latter only
  1653. if appropriate.
  1654. Usage
  1655. =====
  1656. WARNING: Internal method. Do not call directly!
  1657. ``_special_diop_DN(D, N)``: D and N are integers as in `x^2 - Dy^2 = N`.
  1658. Details
  1659. =======
  1660. ``D`` and ``N`` correspond to D and N in the equation.
  1661. Examples
  1662. ========
  1663. >>> from sympy.solvers.diophantine.diophantine import _special_diop_DN
  1664. >>> _special_diop_DN(13, -3) # Solves equation x**2 - 13*y**2 = -3
  1665. [(7, 2), (137, 38)]
  1666. The output can be interpreted as follows: There are two fundamental
  1667. solutions to the equation `x^2 - 13y^2 = -3` given by (7, 2) and
  1668. (137, 38). Each tuple is in the form (x, y), i.e. solution (7, 2) means
  1669. that `x = 7` and `y = 2`.
  1670. >>> _special_diop_DN(2445, -20) # Solves equation x**2 - 2445*y**2 = -20
  1671. [(445, 9), (17625560, 356454), (698095554475, 14118073569)]
  1672. See Also
  1673. ========
  1674. diop_DN()
  1675. References
  1676. ==========
  1677. .. [1] Section 4.4.4 of the following book:
  1678. Quadratic Diophantine Equations, T. Andreescu and D. Andrica,
  1679. Springer, 2015.
  1680. """
  1681. # The following assertion was removed for efficiency, with the understanding
  1682. # that this method is not called directly. The parent method, `diop_DN`
  1683. # is responsible for performing the appropriate checks.
  1684. #
  1685. # assert (1 < N**2 < D) and (not integer_nthroot(D, 2)[1])
  1686. sqrt_D = sqrt(D)
  1687. F = [(N, 1)]
  1688. f = 2
  1689. while True:
  1690. f2 = f**2
  1691. if f2 > abs(N):
  1692. break
  1693. n, r = divmod(N, f2)
  1694. if r == 0:
  1695. F.append((n, f))
  1696. f += 1
  1697. P = 0
  1698. Q = 1
  1699. G0, G1 = 0, 1
  1700. B0, B1 = 1, 0
  1701. solutions = []
  1702. i = 0
  1703. while True:
  1704. a = floor((P + sqrt_D) / Q)
  1705. P = a*Q - P
  1706. Q = (D - P**2) // Q
  1707. G2 = a*G1 + G0
  1708. B2 = a*B1 + B0
  1709. for n, f in F:
  1710. if G2**2 - D*B2**2 == n:
  1711. solutions.append((f*G2, f*B2))
  1712. i += 1
  1713. if Q == 1 and i % 2 == 0:
  1714. break
  1715. G0, G1 = G1, G2
  1716. B0, B1 = B1, B2
  1717. return solutions
  1718. def cornacchia(a, b, m):
  1719. r"""
  1720. Solves `ax^2 + by^2 = m` where `\gcd(a, b) = 1 = gcd(a, m)` and `a, b > 0`.
  1721. Explanation
  1722. ===========
  1723. Uses the algorithm due to Cornacchia. The method only finds primitive
  1724. solutions, i.e. ones with `\gcd(x, y) = 1`. So this method cannot be used to
  1725. find the solutions of `x^2 + y^2 = 20` since the only solution to former is
  1726. `(x, y) = (4, 2)` and it is not primitive. When `a = b`, only the
  1727. solutions with `x \leq y` are found. For more details, see the References.
  1728. Examples
  1729. ========
  1730. >>> from sympy.solvers.diophantine.diophantine import cornacchia
  1731. >>> cornacchia(2, 3, 35) # equation 2x**2 + 3y**2 = 35
  1732. {(2, 3), (4, 1)}
  1733. >>> cornacchia(1, 1, 25) # equation x**2 + y**2 = 25
  1734. {(4, 3)}
  1735. References
  1736. ===========
  1737. .. [1] A. Nitaj, "L'algorithme de Cornacchia"
  1738. .. [2] Solving the diophantine equation ax**2 + by**2 = m by Cornacchia's
  1739. method, [online], Available:
  1740. http://www.numbertheory.org/php/cornacchia.html
  1741. See Also
  1742. ========
  1743. sympy.utilities.iterables.signed_permutations
  1744. """
  1745. sols = set()
  1746. a1 = igcdex(a, m)[0]
  1747. v = sqrt_mod(-b*a1, m, all_roots=True)
  1748. if not v:
  1749. return None
  1750. for t in v:
  1751. if t < m // 2:
  1752. continue
  1753. u, r = t, m
  1754. while True:
  1755. u, r = r, u % r
  1756. if a*r**2 < m:
  1757. break
  1758. m1 = m - a*r**2
  1759. if m1 % b == 0:
  1760. m1 = m1 // b
  1761. s, _exact = integer_nthroot(m1, 2)
  1762. if _exact:
  1763. if a == b and r < s:
  1764. r, s = s, r
  1765. sols.add((int(r), int(s)))
  1766. return sols
  1767. def PQa(P_0, Q_0, D):
  1768. r"""
  1769. Returns useful information needed to solve the Pell equation.
  1770. Explanation
  1771. ===========
  1772. There are six sequences of integers defined related to the continued
  1773. fraction representation of `\\frac{P + \sqrt{D}}{Q}`, namely {`P_{i}`},
  1774. {`Q_{i}`}, {`a_{i}`},{`A_{i}`}, {`B_{i}`}, {`G_{i}`}. ``PQa()`` Returns
  1775. these values as a 6-tuple in the same order as mentioned above. Refer [1]_
  1776. for more detailed information.
  1777. Usage
  1778. =====
  1779. ``PQa(P_0, Q_0, D)``: ``P_0``, ``Q_0`` and ``D`` are integers corresponding
  1780. to `P_{0}`, `Q_{0}` and `D` in the continued fraction
  1781. `\\frac{P_{0} + \sqrt{D}}{Q_{0}}`.
  1782. Also it's assumed that `P_{0}^2 == D mod(|Q_{0}|)` and `D` is square free.
  1783. Examples
  1784. ========
  1785. >>> from sympy.solvers.diophantine.diophantine import PQa
  1786. >>> pqa = PQa(13, 4, 5) # (13 + sqrt(5))/4
  1787. >>> next(pqa) # (P_0, Q_0, a_0, A_0, B_0, G_0)
  1788. (13, 4, 3, 3, 1, -1)
  1789. >>> next(pqa) # (P_1, Q_1, a_1, A_1, B_1, G_1)
  1790. (-1, 1, 1, 4, 1, 3)
  1791. References
  1792. ==========
  1793. .. [1] Solving the generalized Pell equation x^2 - Dy^2 = N, John P.
  1794. Robertson, July 31, 2004, Pages 4 - 8. https://web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
  1795. """
  1796. A_i_2 = B_i_1 = 0
  1797. A_i_1 = B_i_2 = 1
  1798. G_i_2 = -P_0
  1799. G_i_1 = Q_0
  1800. P_i = P_0
  1801. Q_i = Q_0
  1802. while True:
  1803. a_i = floor((P_i + sqrt(D))/Q_i)
  1804. A_i = a_i*A_i_1 + A_i_2
  1805. B_i = a_i*B_i_1 + B_i_2
  1806. G_i = a_i*G_i_1 + G_i_2
  1807. yield P_i, Q_i, a_i, A_i, B_i, G_i
  1808. A_i_1, A_i_2 = A_i, A_i_1
  1809. B_i_1, B_i_2 = B_i, B_i_1
  1810. G_i_1, G_i_2 = G_i, G_i_1
  1811. P_i = a_i*Q_i - P_i
  1812. Q_i = (D - P_i**2)/Q_i
  1813. def diop_bf_DN(D, N, t=symbols("t", integer=True)):
  1814. r"""
  1815. Uses brute force to solve the equation, `x^2 - Dy^2 = N`.
  1816. Explanation
  1817. ===========
  1818. Mainly concerned with the generalized Pell equation which is the case when
  1819. `D > 0, D` is not a perfect square. For more information on the case refer
  1820. [1]_. Let `(t, u)` be the minimal positive solution of the equation
  1821. `x^2 - Dy^2 = 1`. Then this method requires
  1822. `\sqrt{\\frac{\mid N \mid (t \pm 1)}{2D}}` to be small.
  1823. Usage
  1824. =====
  1825. ``diop_bf_DN(D, N, t)``: ``D`` and ``N`` are coefficients in
  1826. `x^2 - Dy^2 = N` and ``t`` is the parameter to be used in the solutions.
  1827. Details
  1828. =======
  1829. ``D`` and ``N`` correspond to D and N in the equation.
  1830. ``t`` is the parameter to be used in the solutions.
  1831. Examples
  1832. ========
  1833. >>> from sympy.solvers.diophantine.diophantine import diop_bf_DN
  1834. >>> diop_bf_DN(13, -4)
  1835. [(3, 1), (-3, 1), (36, 10)]
  1836. >>> diop_bf_DN(986, 1)
  1837. [(49299, 1570)]
  1838. See Also
  1839. ========
  1840. diop_DN()
  1841. References
  1842. ==========
  1843. .. [1] Solving the generalized Pell equation x**2 - D*y**2 = N, John P.
  1844. Robertson, July 31, 2004, Page 15. https://web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
  1845. """
  1846. D = as_int(D)
  1847. N = as_int(N)
  1848. sol = []
  1849. a = diop_DN(D, 1)
  1850. u = a[0][0]
  1851. if abs(N) == 1:
  1852. return diop_DN(D, N)
  1853. elif N > 1:
  1854. L1 = 0
  1855. L2 = integer_nthroot(int(N*(u - 1)/(2*D)), 2)[0] + 1
  1856. elif N < -1:
  1857. L1, _exact = integer_nthroot(-int(N/D), 2)
  1858. if not _exact:
  1859. L1 += 1
  1860. L2 = integer_nthroot(-int(N*(u + 1)/(2*D)), 2)[0] + 1
  1861. else: # N = 0
  1862. if D < 0:
  1863. return [(0, 0)]
  1864. elif D == 0:
  1865. return [(0, t)]
  1866. else:
  1867. sD, _exact = integer_nthroot(D, 2)
  1868. if _exact:
  1869. return [(sD*t, t), (-sD*t, t)]
  1870. else:
  1871. return [(0, 0)]
  1872. for y in range(L1, L2):
  1873. try:
  1874. x, _exact = integer_nthroot(N + D*y**2, 2)
  1875. except ValueError:
  1876. _exact = False
  1877. if _exact:
  1878. sol.append((x, y))
  1879. if not equivalent(x, y, -x, y, D, N):
  1880. sol.append((-x, y))
  1881. return sol
  1882. def equivalent(u, v, r, s, D, N):
  1883. """
  1884. Returns True if two solutions `(u, v)` and `(r, s)` of `x^2 - Dy^2 = N`
  1885. belongs to the same equivalence class and False otherwise.
  1886. Explanation
  1887. ===========
  1888. Two solutions `(u, v)` and `(r, s)` to the above equation fall to the same
  1889. equivalence class iff both `(ur - Dvs)` and `(us - vr)` are divisible by
  1890. `N`. See reference [1]_. No test is performed to test whether `(u, v)` and
  1891. `(r, s)` are actually solutions to the equation. User should take care of
  1892. this.
  1893. Usage
  1894. =====
  1895. ``equivalent(u, v, r, s, D, N)``: `(u, v)` and `(r, s)` are two solutions
  1896. of the equation `x^2 - Dy^2 = N` and all parameters involved are integers.
  1897. Examples
  1898. ========
  1899. >>> from sympy.solvers.diophantine.diophantine import equivalent
  1900. >>> equivalent(18, 5, -18, -5, 13, -1)
  1901. True
  1902. >>> equivalent(3, 1, -18, 393, 109, -4)
  1903. False
  1904. References
  1905. ==========
  1906. .. [1] Solving the generalized Pell equation x**2 - D*y**2 = N, John P.
  1907. Robertson, July 31, 2004, Page 12. https://web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
  1908. """
  1909. return divisible(u*r - D*v*s, N) and divisible(u*s - v*r, N)
  1910. def length(P, Q, D):
  1911. r"""
  1912. Returns the (length of aperiodic part + length of periodic part) of
  1913. continued fraction representation of `\\frac{P + \sqrt{D}}{Q}`.
  1914. It is important to remember that this does NOT return the length of the
  1915. periodic part but the sum of the lengths of the two parts as mentioned
  1916. above.
  1917. Usage
  1918. =====
  1919. ``length(P, Q, D)``: ``P``, ``Q`` and ``D`` are integers corresponding to
  1920. the continued fraction `\\frac{P + \sqrt{D}}{Q}`.
  1921. Details
  1922. =======
  1923. ``P``, ``D`` and ``Q`` corresponds to P, D and Q in the continued fraction,
  1924. `\\frac{P + \sqrt{D}}{Q}`.
  1925. Examples
  1926. ========
  1927. >>> from sympy.solvers.diophantine.diophantine import length
  1928. >>> length(-2, 4, 5) # (-2 + sqrt(5))/4
  1929. 3
  1930. >>> length(-5, 4, 17) # (-5 + sqrt(17))/4
  1931. 4
  1932. See Also
  1933. ========
  1934. sympy.ntheory.continued_fraction.continued_fraction_periodic
  1935. """
  1936. from sympy.ntheory.continued_fraction import continued_fraction_periodic
  1937. v = continued_fraction_periodic(P, Q, D)
  1938. if isinstance(v[-1], list):
  1939. rpt = len(v[-1])
  1940. nonrpt = len(v) - 1
  1941. else:
  1942. rpt = 0
  1943. nonrpt = len(v)
  1944. return rpt + nonrpt
  1945. def transformation_to_DN(eq):
  1946. """
  1947. This function transforms general quadratic,
  1948. `ax^2 + bxy + cy^2 + dx + ey + f = 0`
  1949. to more easy to deal with `X^2 - DY^2 = N` form.
  1950. Explanation
  1951. ===========
  1952. This is used to solve the general quadratic equation by transforming it to
  1953. the latter form. Refer to [1]_ for more detailed information on the
  1954. transformation. This function returns a tuple (A, B) where A is a 2 X 2
  1955. matrix and B is a 2 X 1 matrix such that,
  1956. Transpose([x y]) = A * Transpose([X Y]) + B
  1957. Usage
  1958. =====
  1959. ``transformation_to_DN(eq)``: where ``eq`` is the quadratic to be
  1960. transformed.
  1961. Examples
  1962. ========
  1963. >>> from sympy.abc import x, y
  1964. >>> from sympy.solvers.diophantine.diophantine import transformation_to_DN
  1965. >>> A, B = transformation_to_DN(x**2 - 3*x*y - y**2 - 2*y + 1)
  1966. >>> A
  1967. Matrix([
  1968. [1/26, 3/26],
  1969. [ 0, 1/13]])
  1970. >>> B
  1971. Matrix([
  1972. [-6/13],
  1973. [-4/13]])
  1974. A, B returned are such that Transpose((x y)) = A * Transpose((X Y)) + B.
  1975. Substituting these values for `x` and `y` and a bit of simplifying work
  1976. will give an equation of the form `x^2 - Dy^2 = N`.
  1977. >>> from sympy.abc import X, Y
  1978. >>> from sympy import Matrix, simplify
  1979. >>> u = (A*Matrix([X, Y]) + B)[0] # Transformation for x
  1980. >>> u
  1981. X/26 + 3*Y/26 - 6/13
  1982. >>> v = (A*Matrix([X, Y]) + B)[1] # Transformation for y
  1983. >>> v
  1984. Y/13 - 4/13
  1985. Next we will substitute these formulas for `x` and `y` and do
  1986. ``simplify()``.
  1987. >>> eq = simplify((x**2 - 3*x*y - y**2 - 2*y + 1).subs(zip((x, y), (u, v))))
  1988. >>> eq
  1989. X**2/676 - Y**2/52 + 17/13
  1990. By multiplying the denominator appropriately, we can get a Pell equation
  1991. in the standard form.
  1992. >>> eq * 676
  1993. X**2 - 13*Y**2 + 884
  1994. If only the final equation is needed, ``find_DN()`` can be used.
  1995. See Also
  1996. ========
  1997. find_DN()
  1998. References
  1999. ==========
  2000. .. [1] Solving the equation ax^2 + bxy + cy^2 + dx + ey + f = 0,
  2001. John P.Robertson, May 8, 2003, Page 7 - 11.
  2002. https://web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
  2003. """
  2004. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2005. if diop_type == BinaryQuadratic.name:
  2006. return _transformation_to_DN(var, coeff)
  2007. def _transformation_to_DN(var, coeff):
  2008. x, y = var
  2009. a = coeff[x**2]
  2010. b = coeff[x*y]
  2011. c = coeff[y**2]
  2012. d = coeff[x]
  2013. e = coeff[y]
  2014. f = coeff[1]
  2015. a, b, c, d, e, f = [as_int(i) for i in _remove_gcd(a, b, c, d, e, f)]
  2016. X, Y = symbols("X, Y", integer=True)
  2017. if b:
  2018. B, C = _rational_pq(2*a, b)
  2019. A, T = _rational_pq(a, B**2)
  2020. # eq_1 = A*B*X**2 + B*(c*T - A*C**2)*Y**2 + d*T*X + (B*e*T - d*T*C)*Y + f*T*B
  2021. coeff = {X**2: A*B, X*Y: 0, Y**2: B*(c*T - A*C**2), X: d*T, Y: B*e*T - d*T*C, 1: f*T*B}
  2022. A_0, B_0 = _transformation_to_DN([X, Y], coeff)
  2023. return Matrix(2, 2, [S.One/B, -S(C)/B, 0, 1])*A_0, Matrix(2, 2, [S.One/B, -S(C)/B, 0, 1])*B_0
  2024. else:
  2025. if d:
  2026. B, C = _rational_pq(2*a, d)
  2027. A, T = _rational_pq(a, B**2)
  2028. # eq_2 = A*X**2 + c*T*Y**2 + e*T*Y + f*T - A*C**2
  2029. coeff = {X**2: A, X*Y: 0, Y**2: c*T, X: 0, Y: e*T, 1: f*T - A*C**2}
  2030. A_0, B_0 = _transformation_to_DN([X, Y], coeff)
  2031. return Matrix(2, 2, [S.One/B, 0, 0, 1])*A_0, Matrix(2, 2, [S.One/B, 0, 0, 1])*B_0 + Matrix([-S(C)/B, 0])
  2032. else:
  2033. if e:
  2034. B, C = _rational_pq(2*c, e)
  2035. A, T = _rational_pq(c, B**2)
  2036. # eq_3 = a*T*X**2 + A*Y**2 + f*T - A*C**2
  2037. coeff = {X**2: a*T, X*Y: 0, Y**2: A, X: 0, Y: 0, 1: f*T - A*C**2}
  2038. A_0, B_0 = _transformation_to_DN([X, Y], coeff)
  2039. return Matrix(2, 2, [1, 0, 0, S.One/B])*A_0, Matrix(2, 2, [1, 0, 0, S.One/B])*B_0 + Matrix([0, -S(C)/B])
  2040. else:
  2041. # TODO: pre-simplification: Not necessary but may simplify
  2042. # the equation.
  2043. return Matrix(2, 2, [S.One/a, 0, 0, 1]), Matrix([0, 0])
  2044. def find_DN(eq):
  2045. """
  2046. This function returns a tuple, `(D, N)` of the simplified form,
  2047. `x^2 - Dy^2 = N`, corresponding to the general quadratic,
  2048. `ax^2 + bxy + cy^2 + dx + ey + f = 0`.
  2049. Solving the general quadratic is then equivalent to solving the equation
  2050. `X^2 - DY^2 = N` and transforming the solutions by using the transformation
  2051. matrices returned by ``transformation_to_DN()``.
  2052. Usage
  2053. =====
  2054. ``find_DN(eq)``: where ``eq`` is the quadratic to be transformed.
  2055. Examples
  2056. ========
  2057. >>> from sympy.abc import x, y
  2058. >>> from sympy.solvers.diophantine.diophantine import find_DN
  2059. >>> find_DN(x**2 - 3*x*y - y**2 - 2*y + 1)
  2060. (13, -884)
  2061. Interpretation of the output is that we get `X^2 -13Y^2 = -884` after
  2062. transforming `x^2 - 3xy - y^2 - 2y + 1` using the transformation returned
  2063. by ``transformation_to_DN()``.
  2064. See Also
  2065. ========
  2066. transformation_to_DN()
  2067. References
  2068. ==========
  2069. .. [1] Solving the equation ax^2 + bxy + cy^2 + dx + ey + f = 0,
  2070. John P.Robertson, May 8, 2003, Page 7 - 11.
  2071. https://web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
  2072. """
  2073. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2074. if diop_type == BinaryQuadratic.name:
  2075. return _find_DN(var, coeff)
  2076. def _find_DN(var, coeff):
  2077. x, y = var
  2078. X, Y = symbols("X, Y", integer=True)
  2079. A, B = _transformation_to_DN(var, coeff)
  2080. u = (A*Matrix([X, Y]) + B)[0]
  2081. v = (A*Matrix([X, Y]) + B)[1]
  2082. eq = x**2*coeff[x**2] + x*y*coeff[x*y] + y**2*coeff[y**2] + x*coeff[x] + y*coeff[y] + coeff[1]
  2083. simplified = _mexpand(eq.subs(zip((x, y), (u, v))))
  2084. coeff = simplified.as_coefficients_dict()
  2085. return -coeff[Y**2]/coeff[X**2], -coeff[1]/coeff[X**2]
  2086. def check_param(x, y, a, params):
  2087. """
  2088. If there is a number modulo ``a`` such that ``x`` and ``y`` are both
  2089. integers, then return a parametric representation for ``x`` and ``y``
  2090. else return (None, None).
  2091. Here ``x`` and ``y`` are functions of ``t``.
  2092. """
  2093. from sympy.simplify.simplify import clear_coefficients
  2094. if x.is_number and not x.is_Integer:
  2095. return DiophantineSolutionSet([x, y], parameters=params)
  2096. if y.is_number and not y.is_Integer:
  2097. return DiophantineSolutionSet([x, y], parameters=params)
  2098. m, n = symbols("m, n", integer=True)
  2099. c, p = (m*x + n*y).as_content_primitive()
  2100. if a % c.q:
  2101. return DiophantineSolutionSet([x, y], parameters=params)
  2102. # clear_coefficients(mx + b, R)[1] -> (R - b)/m
  2103. eq = clear_coefficients(x, m)[1] - clear_coefficients(y, n)[1]
  2104. junk, eq = eq.as_content_primitive()
  2105. return _diop_solve(eq, params=params)
  2106. def diop_ternary_quadratic(eq, parameterize=False):
  2107. """
  2108. Solves the general quadratic ternary form,
  2109. `ax^2 + by^2 + cz^2 + fxy + gyz + hxz = 0`.
  2110. Returns a tuple `(x, y, z)` which is a base solution for the above
  2111. equation. If there are no solutions, `(None, None, None)` is returned.
  2112. Usage
  2113. =====
  2114. ``diop_ternary_quadratic(eq)``: Return a tuple containing a basic solution
  2115. to ``eq``.
  2116. Details
  2117. =======
  2118. ``eq`` should be an homogeneous expression of degree two in three variables
  2119. and it is assumed to be zero.
  2120. Examples
  2121. ========
  2122. >>> from sympy.abc import x, y, z
  2123. >>> from sympy.solvers.diophantine.diophantine import diop_ternary_quadratic
  2124. >>> diop_ternary_quadratic(x**2 + 3*y**2 - z**2)
  2125. (1, 0, 1)
  2126. >>> diop_ternary_quadratic(4*x**2 + 5*y**2 - z**2)
  2127. (1, 0, 2)
  2128. >>> diop_ternary_quadratic(45*x**2 - 7*y**2 - 8*x*y - z**2)
  2129. (28, 45, 105)
  2130. >>> diop_ternary_quadratic(x**2 - 49*y**2 - z**2 + 13*z*y -8*x*y)
  2131. (9, 1, 5)
  2132. """
  2133. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2134. if diop_type in (
  2135. HomogeneousTernaryQuadratic.name,
  2136. HomogeneousTernaryQuadraticNormal.name):
  2137. sol = _diop_ternary_quadratic(var, coeff)
  2138. if len(sol) > 0:
  2139. x_0, y_0, z_0 = list(sol)[0]
  2140. else:
  2141. x_0, y_0, z_0 = None, None, None
  2142. if parameterize:
  2143. return _parametrize_ternary_quadratic(
  2144. (x_0, y_0, z_0), var, coeff)
  2145. return x_0, y_0, z_0
  2146. def _diop_ternary_quadratic(_var, coeff):
  2147. eq = sum([i*coeff[i] for i in coeff])
  2148. if HomogeneousTernaryQuadratic(eq).matches():
  2149. return HomogeneousTernaryQuadratic(eq, free_symbols=_var).solve()
  2150. elif HomogeneousTernaryQuadraticNormal(eq).matches():
  2151. return HomogeneousTernaryQuadraticNormal(eq, free_symbols=_var).solve()
  2152. def transformation_to_normal(eq):
  2153. """
  2154. Returns the transformation Matrix that converts a general ternary
  2155. quadratic equation ``eq`` (`ax^2 + by^2 + cz^2 + dxy + eyz + fxz`)
  2156. to a form without cross terms: `ax^2 + by^2 + cz^2 = 0`. This is
  2157. not used in solving ternary quadratics; it is only implemented for
  2158. the sake of completeness.
  2159. """
  2160. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2161. if diop_type in (
  2162. "homogeneous_ternary_quadratic",
  2163. "homogeneous_ternary_quadratic_normal"):
  2164. return _transformation_to_normal(var, coeff)
  2165. def _transformation_to_normal(var, coeff):
  2166. _var = list(var) # copy
  2167. x, y, z = var
  2168. if not any(coeff[i**2] for i in var):
  2169. # https://math.stackexchange.com/questions/448051/transform-quadratic-ternary-form-to-normal-form/448065#448065
  2170. a = coeff[x*y]
  2171. b = coeff[y*z]
  2172. c = coeff[x*z]
  2173. swap = False
  2174. if not a: # b can't be 0 or else there aren't 3 vars
  2175. swap = True
  2176. a, b = b, a
  2177. T = Matrix(((1, 1, -b/a), (1, -1, -c/a), (0, 0, 1)))
  2178. if swap:
  2179. T.row_swap(0, 1)
  2180. T.col_swap(0, 1)
  2181. return T
  2182. if coeff[x**2] == 0:
  2183. # If the coefficient of x is zero change the variables
  2184. if coeff[y**2] == 0:
  2185. _var[0], _var[2] = var[2], var[0]
  2186. T = _transformation_to_normal(_var, coeff)
  2187. T.row_swap(0, 2)
  2188. T.col_swap(0, 2)
  2189. return T
  2190. else:
  2191. _var[0], _var[1] = var[1], var[0]
  2192. T = _transformation_to_normal(_var, coeff)
  2193. T.row_swap(0, 1)
  2194. T.col_swap(0, 1)
  2195. return T
  2196. # Apply the transformation x --> X - (B*Y + C*Z)/(2*A)
  2197. if coeff[x*y] != 0 or coeff[x*z] != 0:
  2198. A = coeff[x**2]
  2199. B = coeff[x*y]
  2200. C = coeff[x*z]
  2201. D = coeff[y**2]
  2202. E = coeff[y*z]
  2203. F = coeff[z**2]
  2204. _coeff = {}
  2205. _coeff[x**2] = 4*A**2
  2206. _coeff[y**2] = 4*A*D - B**2
  2207. _coeff[z**2] = 4*A*F - C**2
  2208. _coeff[y*z] = 4*A*E - 2*B*C
  2209. _coeff[x*y] = 0
  2210. _coeff[x*z] = 0
  2211. T_0 = _transformation_to_normal(_var, _coeff)
  2212. return Matrix(3, 3, [1, S(-B)/(2*A), S(-C)/(2*A), 0, 1, 0, 0, 0, 1])*T_0
  2213. elif coeff[y*z] != 0:
  2214. if coeff[y**2] == 0:
  2215. if coeff[z**2] == 0:
  2216. # Equations of the form A*x**2 + E*yz = 0.
  2217. # Apply transformation y -> Y + Z ans z -> Y - Z
  2218. return Matrix(3, 3, [1, 0, 0, 0, 1, 1, 0, 1, -1])
  2219. else:
  2220. # Ax**2 + E*y*z + F*z**2 = 0
  2221. _var[0], _var[2] = var[2], var[0]
  2222. T = _transformation_to_normal(_var, coeff)
  2223. T.row_swap(0, 2)
  2224. T.col_swap(0, 2)
  2225. return T
  2226. else:
  2227. # A*x**2 + D*y**2 + E*y*z + F*z**2 = 0, F may be zero
  2228. _var[0], _var[1] = var[1], var[0]
  2229. T = _transformation_to_normal(_var, coeff)
  2230. T.row_swap(0, 1)
  2231. T.col_swap(0, 1)
  2232. return T
  2233. else:
  2234. return Matrix.eye(3)
  2235. def parametrize_ternary_quadratic(eq):
  2236. """
  2237. Returns the parametrized general solution for the ternary quadratic
  2238. equation ``eq`` which has the form
  2239. `ax^2 + by^2 + cz^2 + fxy + gyz + hxz = 0`.
  2240. Examples
  2241. ========
  2242. >>> from sympy import Tuple, ordered
  2243. >>> from sympy.abc import x, y, z
  2244. >>> from sympy.solvers.diophantine.diophantine import parametrize_ternary_quadratic
  2245. The parametrized solution may be returned with three parameters:
  2246. >>> parametrize_ternary_quadratic(2*x**2 + y**2 - 2*z**2)
  2247. (p**2 - 2*q**2, -2*p**2 + 4*p*q - 4*p*r - 4*q**2, p**2 - 4*p*q + 2*q**2 - 4*q*r)
  2248. There might also be only two parameters:
  2249. >>> parametrize_ternary_quadratic(4*x**2 + 2*y**2 - 3*z**2)
  2250. (2*p**2 - 3*q**2, -4*p**2 + 12*p*q - 6*q**2, 4*p**2 - 8*p*q + 6*q**2)
  2251. Notes
  2252. =====
  2253. Consider ``p`` and ``q`` in the previous 2-parameter
  2254. solution and observe that more than one solution can be represented
  2255. by a given pair of parameters. If `p` and ``q`` are not coprime, this is
  2256. trivially true since the common factor will also be a common factor of the
  2257. solution values. But it may also be true even when ``p`` and
  2258. ``q`` are coprime:
  2259. >>> sol = Tuple(*_)
  2260. >>> p, q = ordered(sol.free_symbols)
  2261. >>> sol.subs([(p, 3), (q, 2)])
  2262. (6, 12, 12)
  2263. >>> sol.subs([(q, 1), (p, 1)])
  2264. (-1, 2, 2)
  2265. >>> sol.subs([(q, 0), (p, 1)])
  2266. (2, -4, 4)
  2267. >>> sol.subs([(q, 1), (p, 0)])
  2268. (-3, -6, 6)
  2269. Except for sign and a common factor, these are equivalent to
  2270. the solution of (1, 2, 2).
  2271. References
  2272. ==========
  2273. .. [1] The algorithmic resolution of Diophantine equations, Nigel P. Smart,
  2274. London Mathematical Society Student Texts 41, Cambridge University
  2275. Press, Cambridge, 1998.
  2276. """
  2277. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2278. if diop_type in (
  2279. "homogeneous_ternary_quadratic",
  2280. "homogeneous_ternary_quadratic_normal"):
  2281. x_0, y_0, z_0 = list(_diop_ternary_quadratic(var, coeff))[0]
  2282. return _parametrize_ternary_quadratic(
  2283. (x_0, y_0, z_0), var, coeff)
  2284. def _parametrize_ternary_quadratic(solution, _var, coeff):
  2285. # called for a*x**2 + b*y**2 + c*z**2 + d*x*y + e*y*z + f*x*z = 0
  2286. assert 1 not in coeff
  2287. x_0, y_0, z_0 = solution
  2288. v = list(_var) # copy
  2289. if x_0 is None:
  2290. return (None, None, None)
  2291. if solution.count(0) >= 2:
  2292. # if there are 2 zeros the equation reduces
  2293. # to k*X**2 == 0 where X is x, y, or z so X must
  2294. # be zero, too. So there is only the trivial
  2295. # solution.
  2296. return (None, None, None)
  2297. if x_0 == 0:
  2298. v[0], v[1] = v[1], v[0]
  2299. y_p, x_p, z_p = _parametrize_ternary_quadratic(
  2300. (y_0, x_0, z_0), v, coeff)
  2301. return x_p, y_p, z_p
  2302. x, y, z = v
  2303. r, p, q = symbols("r, p, q", integer=True)
  2304. eq = sum(k*v for k, v in coeff.items())
  2305. eq_1 = _mexpand(eq.subs(zip(
  2306. (x, y, z), (r*x_0, r*y_0 + p, r*z_0 + q))))
  2307. A, B = eq_1.as_independent(r, as_Add=True)
  2308. x = A*x_0
  2309. y = (A*y_0 - _mexpand(B/r*p))
  2310. z = (A*z_0 - _mexpand(B/r*q))
  2311. return _remove_gcd(x, y, z)
  2312. def diop_ternary_quadratic_normal(eq, parameterize=False):
  2313. """
  2314. Solves the quadratic ternary diophantine equation,
  2315. `ax^2 + by^2 + cz^2 = 0`.
  2316. Explanation
  2317. ===========
  2318. Here the coefficients `a`, `b`, and `c` should be non zero. Otherwise the
  2319. equation will be a quadratic binary or univariate equation. If solvable,
  2320. returns a tuple `(x, y, z)` that satisfies the given equation. If the
  2321. equation does not have integer solutions, `(None, None, None)` is returned.
  2322. Usage
  2323. =====
  2324. ``diop_ternary_quadratic_normal(eq)``: where ``eq`` is an equation of the form
  2325. `ax^2 + by^2 + cz^2 = 0`.
  2326. Examples
  2327. ========
  2328. >>> from sympy.abc import x, y, z
  2329. >>> from sympy.solvers.diophantine.diophantine import diop_ternary_quadratic_normal
  2330. >>> diop_ternary_quadratic_normal(x**2 + 3*y**2 - z**2)
  2331. (1, 0, 1)
  2332. >>> diop_ternary_quadratic_normal(4*x**2 + 5*y**2 - z**2)
  2333. (1, 0, 2)
  2334. >>> diop_ternary_quadratic_normal(34*x**2 - 3*y**2 - 301*z**2)
  2335. (4, 9, 1)
  2336. """
  2337. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2338. if diop_type == HomogeneousTernaryQuadraticNormal.name:
  2339. sol = _diop_ternary_quadratic_normal(var, coeff)
  2340. if len(sol) > 0:
  2341. x_0, y_0, z_0 = list(sol)[0]
  2342. else:
  2343. x_0, y_0, z_0 = None, None, None
  2344. if parameterize:
  2345. return _parametrize_ternary_quadratic(
  2346. (x_0, y_0, z_0), var, coeff)
  2347. return x_0, y_0, z_0
  2348. def _diop_ternary_quadratic_normal(var, coeff):
  2349. eq = sum([i * coeff[i] for i in coeff])
  2350. return HomogeneousTernaryQuadraticNormal(eq, free_symbols=var).solve()
  2351. def sqf_normal(a, b, c, steps=False):
  2352. """
  2353. Return `a', b', c'`, the coefficients of the square-free normal
  2354. form of `ax^2 + by^2 + cz^2 = 0`, where `a', b', c'` are pairwise
  2355. prime. If `steps` is True then also return three tuples:
  2356. `sq`, `sqf`, and `(a', b', c')` where `sq` contains the square
  2357. factors of `a`, `b` and `c` after removing the `gcd(a, b, c)`;
  2358. `sqf` contains the values of `a`, `b` and `c` after removing
  2359. both the `gcd(a, b, c)` and the square factors.
  2360. The solutions for `ax^2 + by^2 + cz^2 = 0` can be
  2361. recovered from the solutions of `a'x^2 + b'y^2 + c'z^2 = 0`.
  2362. Examples
  2363. ========
  2364. >>> from sympy.solvers.diophantine.diophantine import sqf_normal
  2365. >>> sqf_normal(2 * 3**2 * 5, 2 * 5 * 11, 2 * 7**2 * 11)
  2366. (11, 1, 5)
  2367. >>> sqf_normal(2 * 3**2 * 5, 2 * 5 * 11, 2 * 7**2 * 11, True)
  2368. ((3, 1, 7), (5, 55, 11), (11, 1, 5))
  2369. References
  2370. ==========
  2371. .. [1] Legendre's Theorem, Legrange's Descent,
  2372. https://public.csusm.edu/aitken_html/notes/legendre.pdf
  2373. See Also
  2374. ========
  2375. reconstruct()
  2376. """
  2377. ABC = _remove_gcd(a, b, c)
  2378. sq = tuple(square_factor(i) for i in ABC)
  2379. sqf = A, B, C = tuple([i//j**2 for i,j in zip(ABC, sq)])
  2380. pc = igcd(A, B)
  2381. A /= pc
  2382. B /= pc
  2383. pa = igcd(B, C)
  2384. B /= pa
  2385. C /= pa
  2386. pb = igcd(A, C)
  2387. A /= pb
  2388. B /= pb
  2389. A *= pa
  2390. B *= pb
  2391. C *= pc
  2392. if steps:
  2393. return (sq, sqf, (A, B, C))
  2394. else:
  2395. return A, B, C
  2396. def square_factor(a):
  2397. r"""
  2398. Returns an integer `c` s.t. `a = c^2k, \ c,k \in Z`. Here `k` is square
  2399. free. `a` can be given as an integer or a dictionary of factors.
  2400. Examples
  2401. ========
  2402. >>> from sympy.solvers.diophantine.diophantine import square_factor
  2403. >>> square_factor(24)
  2404. 2
  2405. >>> square_factor(-36*3)
  2406. 6
  2407. >>> square_factor(1)
  2408. 1
  2409. >>> square_factor({3: 2, 2: 1, -1: 1}) # -18
  2410. 3
  2411. See Also
  2412. ========
  2413. sympy.ntheory.factor_.core
  2414. """
  2415. f = a if isinstance(a, dict) else factorint(a)
  2416. return Mul(*[p**(e//2) for p, e in f.items()])
  2417. def reconstruct(A, B, z):
  2418. """
  2419. Reconstruct the `z` value of an equivalent solution of `ax^2 + by^2 + cz^2`
  2420. from the `z` value of a solution of the square-free normal form of the
  2421. equation, `a'*x^2 + b'*y^2 + c'*z^2`, where `a'`, `b'` and `c'` are square
  2422. free and `gcd(a', b', c') == 1`.
  2423. """
  2424. f = factorint(igcd(A, B))
  2425. for p, e in f.items():
  2426. if e != 1:
  2427. raise ValueError('a and b should be square-free')
  2428. z *= p
  2429. return z
  2430. def ldescent(A, B):
  2431. """
  2432. Return a non-trivial solution to `w^2 = Ax^2 + By^2` using
  2433. Lagrange's method; return None if there is no such solution.
  2434. .
  2435. Here, `A \\neq 0` and `B \\neq 0` and `A` and `B` are square free. Output a
  2436. tuple `(w_0, x_0, y_0)` which is a solution to the above equation.
  2437. Examples
  2438. ========
  2439. >>> from sympy.solvers.diophantine.diophantine import ldescent
  2440. >>> ldescent(1, 1) # w^2 = x^2 + y^2
  2441. (1, 1, 0)
  2442. >>> ldescent(4, -7) # w^2 = 4x^2 - 7y^2
  2443. (2, -1, 0)
  2444. This means that `x = -1, y = 0` and `w = 2` is a solution to the equation
  2445. `w^2 = 4x^2 - 7y^2`
  2446. >>> ldescent(5, -1) # w^2 = 5x^2 - y^2
  2447. (2, 1, -1)
  2448. References
  2449. ==========
  2450. .. [1] The algorithmic resolution of Diophantine equations, Nigel P. Smart,
  2451. London Mathematical Society Student Texts 41, Cambridge University
  2452. Press, Cambridge, 1998.
  2453. .. [2] Efficient Solution of Rational Conices, J. E. Cremona and D. Rusin,
  2454. [online], Available:
  2455. https://nottingham-repository.worktribe.com/output/1023265/efficient-solution-of-rational-conics
  2456. """
  2457. if abs(A) > abs(B):
  2458. w, y, x = ldescent(B, A)
  2459. return w, x, y
  2460. if A == 1:
  2461. return (1, 1, 0)
  2462. if B == 1:
  2463. return (1, 0, 1)
  2464. if B == -1: # and A == -1
  2465. return
  2466. r = sqrt_mod(A, B)
  2467. Q = (r**2 - A) // B
  2468. if Q == 0:
  2469. B_0 = 1
  2470. d = 0
  2471. else:
  2472. div = divisors(Q)
  2473. B_0 = None
  2474. for i in div:
  2475. sQ, _exact = integer_nthroot(abs(Q) // i, 2)
  2476. if _exact:
  2477. B_0, d = sign(Q)*i, sQ
  2478. break
  2479. if B_0 is not None:
  2480. W, X, Y = ldescent(A, B_0)
  2481. return _remove_gcd((-A*X + r*W), (r*X - W), Y*(B_0*d))
  2482. def descent(A, B):
  2483. """
  2484. Returns a non-trivial solution, (x, y, z), to `x^2 = Ay^2 + Bz^2`
  2485. using Lagrange's descent method with lattice-reduction. `A` and `B`
  2486. are assumed to be valid for such a solution to exist.
  2487. This is faster than the normal Lagrange's descent algorithm because
  2488. the Gaussian reduction is used.
  2489. Examples
  2490. ========
  2491. >>> from sympy.solvers.diophantine.diophantine import descent
  2492. >>> descent(3, 1) # x**2 = 3*y**2 + z**2
  2493. (1, 0, 1)
  2494. `(x, y, z) = (1, 0, 1)` is a solution to the above equation.
  2495. >>> descent(41, -113)
  2496. (-16, -3, 1)
  2497. References
  2498. ==========
  2499. .. [1] Efficient Solution of Rational Conices, J. E. Cremona and D. Rusin,
  2500. Mathematics of Computation, Volume 00, Number 0.
  2501. """
  2502. if abs(A) > abs(B):
  2503. x, y, z = descent(B, A)
  2504. return x, z, y
  2505. if B == 1:
  2506. return (1, 0, 1)
  2507. if A == 1:
  2508. return (1, 1, 0)
  2509. if B == -A:
  2510. return (0, 1, 1)
  2511. if B == A:
  2512. x, z, y = descent(-1, A)
  2513. return (A*y, z, x)
  2514. w = sqrt_mod(A, B)
  2515. x_0, z_0 = gaussian_reduce(w, A, B)
  2516. t = (x_0**2 - A*z_0**2) // B
  2517. t_2 = square_factor(t)
  2518. t_1 = t // t_2**2
  2519. x_1, z_1, y_1 = descent(A, t_1)
  2520. return _remove_gcd(x_0*x_1 + A*z_0*z_1, z_0*x_1 + x_0*z_1, t_1*t_2*y_1)
  2521. def gaussian_reduce(w, a, b):
  2522. r"""
  2523. Returns a reduced solution `(x, z)` to the congruence
  2524. `X^2 - aZ^2 \equiv 0 \ (mod \ b)` so that `x^2 + |a|z^2` is minimal.
  2525. Details
  2526. =======
  2527. Here ``w`` is a solution of the congruence `x^2 \equiv a \ (mod \ b)`
  2528. References
  2529. ==========
  2530. .. [1] Gaussian lattice Reduction [online]. Available:
  2531. https://web.archive.org/web/20201021115213/http://home.ie.cuhk.edu.hk/~wkshum/wordpress/?p=404
  2532. .. [2] Efficient Solution of Rational Conices, J. E. Cremona and D. Rusin,
  2533. Mathematics of Computation, Volume 00, Number 0.
  2534. """
  2535. u = (0, 1)
  2536. v = (1, 0)
  2537. if dot(u, v, w, a, b) < 0:
  2538. v = (-v[0], -v[1])
  2539. if norm(u, w, a, b) < norm(v, w, a, b):
  2540. u, v = v, u
  2541. while norm(u, w, a, b) > norm(v, w, a, b):
  2542. k = dot(u, v, w, a, b) // dot(v, v, w, a, b)
  2543. u, v = v, (u[0]- k*v[0], u[1]- k*v[1])
  2544. u, v = v, u
  2545. if dot(u, v, w, a, b) < dot(v, v, w, a, b)/2 or norm((u[0]-v[0], u[1]-v[1]), w, a, b) > norm(v, w, a, b):
  2546. c = v
  2547. else:
  2548. c = (u[0] - v[0], u[1] - v[1])
  2549. return c[0]*w + b*c[1], c[0]
  2550. def dot(u, v, w, a, b):
  2551. r"""
  2552. Returns a special dot product of the vectors `u = (u_{1}, u_{2})` and
  2553. `v = (v_{1}, v_{2})` which is defined in order to reduce solution of
  2554. the congruence equation `X^2 - aZ^2 \equiv 0 \ (mod \ b)`.
  2555. """
  2556. u_1, u_2 = u
  2557. v_1, v_2 = v
  2558. return (w*u_1 + b*u_2)*(w*v_1 + b*v_2) + abs(a)*u_1*v_1
  2559. def norm(u, w, a, b):
  2560. r"""
  2561. Returns the norm of the vector `u = (u_{1}, u_{2})` under the dot product
  2562. defined by `u \cdot v = (wu_{1} + bu_{2})(w*v_{1} + bv_{2}) + |a|*u_{1}*v_{1}`
  2563. where `u = (u_{1}, u_{2})` and `v = (v_{1}, v_{2})`.
  2564. """
  2565. u_1, u_2 = u
  2566. return sqrt(dot((u_1, u_2), (u_1, u_2), w, a, b))
  2567. def holzer(x, y, z, a, b, c):
  2568. r"""
  2569. Simplify the solution `(x, y, z)` of the equation
  2570. `ax^2 + by^2 = cz^2` with `a, b, c > 0` and `z^2 \geq \mid ab \mid` to
  2571. a new reduced solution `(x', y', z')` such that `z'^2 \leq \mid ab \mid`.
  2572. The algorithm is an interpretation of Mordell's reduction as described
  2573. on page 8 of Cremona and Rusin's paper [1]_ and the work of Mordell in
  2574. reference [2]_.
  2575. References
  2576. ==========
  2577. .. [1] Efficient Solution of Rational Conices, J. E. Cremona and D. Rusin,
  2578. Mathematics of Computation, Volume 00, Number 0.
  2579. .. [2] Diophantine Equations, L. J. Mordell, page 48.
  2580. """
  2581. if _odd(c):
  2582. k = 2*c
  2583. else:
  2584. k = c//2
  2585. small = a*b*c
  2586. step = 0
  2587. while True:
  2588. t1, t2, t3 = a*x**2, b*y**2, c*z**2
  2589. # check that it's a solution
  2590. if t1 + t2 != t3:
  2591. if step == 0:
  2592. raise ValueError('bad starting solution')
  2593. break
  2594. x_0, y_0, z_0 = x, y, z
  2595. if max(t1, t2, t3) <= small:
  2596. # Holzer condition
  2597. break
  2598. uv = u, v = base_solution_linear(k, y_0, -x_0)
  2599. if None in uv:
  2600. break
  2601. p, q = -(a*u*x_0 + b*v*y_0), c*z_0
  2602. r = Rational(p, q)
  2603. if _even(c):
  2604. w = _nint_or_floor(p, q)
  2605. assert abs(w - r) <= S.Half
  2606. else:
  2607. w = p//q # floor
  2608. if _odd(a*u + b*v + c*w):
  2609. w += 1
  2610. assert abs(w - r) <= S.One
  2611. A = (a*u**2 + b*v**2 + c*w**2)
  2612. B = (a*u*x_0 + b*v*y_0 + c*w*z_0)
  2613. x = Rational(x_0*A - 2*u*B, k)
  2614. y = Rational(y_0*A - 2*v*B, k)
  2615. z = Rational(z_0*A - 2*w*B, k)
  2616. assert all(i.is_Integer for i in (x, y, z))
  2617. step += 1
  2618. return tuple([int(i) for i in (x_0, y_0, z_0)])
  2619. def diop_general_pythagorean(eq, param=symbols("m", integer=True)):
  2620. """
  2621. Solves the general pythagorean equation,
  2622. `a_{1}^2x_{1}^2 + a_{2}^2x_{2}^2 + . . . + a_{n}^2x_{n}^2 - a_{n + 1}^2x_{n + 1}^2 = 0`.
  2623. Returns a tuple which contains a parametrized solution to the equation,
  2624. sorted in the same order as the input variables.
  2625. Usage
  2626. =====
  2627. ``diop_general_pythagorean(eq, param)``: where ``eq`` is a general
  2628. pythagorean equation which is assumed to be zero and ``param`` is the base
  2629. parameter used to construct other parameters by subscripting.
  2630. Examples
  2631. ========
  2632. >>> from sympy.solvers.diophantine.diophantine import diop_general_pythagorean
  2633. >>> from sympy.abc import a, b, c, d, e
  2634. >>> diop_general_pythagorean(a**2 + b**2 + c**2 - d**2)
  2635. (m1**2 + m2**2 - m3**2, 2*m1*m3, 2*m2*m3, m1**2 + m2**2 + m3**2)
  2636. >>> diop_general_pythagorean(9*a**2 - 4*b**2 + 16*c**2 + 25*d**2 + e**2)
  2637. (10*m1**2 + 10*m2**2 + 10*m3**2 - 10*m4**2, 15*m1**2 + 15*m2**2 + 15*m3**2 + 15*m4**2, 15*m1*m4, 12*m2*m4, 60*m3*m4)
  2638. """
  2639. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2640. if diop_type == GeneralPythagorean.name:
  2641. if param is None:
  2642. params = None
  2643. else:
  2644. params = symbols('%s1:%i' % (param, len(var)), integer=True)
  2645. return list(GeneralPythagorean(eq).solve(parameters=params))[0]
  2646. def diop_general_sum_of_squares(eq, limit=1):
  2647. r"""
  2648. Solves the equation `x_{1}^2 + x_{2}^2 + . . . + x_{n}^2 - k = 0`.
  2649. Returns at most ``limit`` number of solutions.
  2650. Usage
  2651. =====
  2652. ``general_sum_of_squares(eq, limit)`` : Here ``eq`` is an expression which
  2653. is assumed to be zero. Also, ``eq`` should be in the form,
  2654. `x_{1}^2 + x_{2}^2 + . . . + x_{n}^2 - k = 0`.
  2655. Details
  2656. =======
  2657. When `n = 3` if `k = 4^a(8m + 7)` for some `a, m \in Z` then there will be
  2658. no solutions. Refer to [1]_ for more details.
  2659. Examples
  2660. ========
  2661. >>> from sympy.solvers.diophantine.diophantine import diop_general_sum_of_squares
  2662. >>> from sympy.abc import a, b, c, d, e
  2663. >>> diop_general_sum_of_squares(a**2 + b**2 + c**2 + d**2 + e**2 - 2345)
  2664. {(15, 22, 22, 24, 24)}
  2665. Reference
  2666. =========
  2667. .. [1] Representing an integer as a sum of three squares, [online],
  2668. Available:
  2669. https://www.proofwiki.org/wiki/Integer_as_Sum_of_Three_Squares
  2670. """
  2671. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2672. if diop_type == GeneralSumOfSquares.name:
  2673. return set(GeneralSumOfSquares(eq).solve(limit=limit))
  2674. def diop_general_sum_of_even_powers(eq, limit=1):
  2675. """
  2676. Solves the equation `x_{1}^e + x_{2}^e + . . . + x_{n}^e - k = 0`
  2677. where `e` is an even, integer power.
  2678. Returns at most ``limit`` number of solutions.
  2679. Usage
  2680. =====
  2681. ``general_sum_of_even_powers(eq, limit)`` : Here ``eq`` is an expression which
  2682. is assumed to be zero. Also, ``eq`` should be in the form,
  2683. `x_{1}^e + x_{2}^e + . . . + x_{n}^e - k = 0`.
  2684. Examples
  2685. ========
  2686. >>> from sympy.solvers.diophantine.diophantine import diop_general_sum_of_even_powers
  2687. >>> from sympy.abc import a, b
  2688. >>> diop_general_sum_of_even_powers(a**4 + b**4 - (2**4 + 3**4))
  2689. {(2, 3)}
  2690. See Also
  2691. ========
  2692. power_representation
  2693. """
  2694. var, coeff, diop_type = classify_diop(eq, _dict=False)
  2695. if diop_type == GeneralSumOfEvenPowers.name:
  2696. return set(GeneralSumOfEvenPowers(eq).solve(limit=limit))
  2697. ## Functions below this comment can be more suitably grouped under
  2698. ## an Additive number theory module rather than the Diophantine
  2699. ## equation module.
  2700. def partition(n, k=None, zeros=False):
  2701. """
  2702. Returns a generator that can be used to generate partitions of an integer
  2703. `n`.
  2704. Explanation
  2705. ===========
  2706. A partition of `n` is a set of positive integers which add up to `n`. For
  2707. example, partitions of 3 are 3, 1 + 2, 1 + 1 + 1. A partition is returned
  2708. as a tuple. If ``k`` equals None, then all possible partitions are returned
  2709. irrespective of their size, otherwise only the partitions of size ``k`` are
  2710. returned. If the ``zero`` parameter is set to True then a suitable
  2711. number of zeros are added at the end of every partition of size less than
  2712. ``k``.
  2713. ``zero`` parameter is considered only if ``k`` is not None. When the
  2714. partitions are over, the last `next()` call throws the ``StopIteration``
  2715. exception, so this function should always be used inside a try - except
  2716. block.
  2717. Details
  2718. =======
  2719. ``partition(n, k)``: Here ``n`` is a positive integer and ``k`` is the size
  2720. of the partition which is also positive integer.
  2721. Examples
  2722. ========
  2723. >>> from sympy.solvers.diophantine.diophantine import partition
  2724. >>> f = partition(5)
  2725. >>> next(f)
  2726. (1, 1, 1, 1, 1)
  2727. >>> next(f)
  2728. (1, 1, 1, 2)
  2729. >>> g = partition(5, 3)
  2730. >>> next(g)
  2731. (1, 1, 3)
  2732. >>> next(g)
  2733. (1, 2, 2)
  2734. >>> g = partition(5, 3, zeros=True)
  2735. >>> next(g)
  2736. (0, 0, 5)
  2737. """
  2738. if not zeros or k is None:
  2739. for i in ordered_partitions(n, k):
  2740. yield tuple(i)
  2741. else:
  2742. for m in range(1, k + 1):
  2743. for i in ordered_partitions(n, m):
  2744. i = tuple(i)
  2745. yield (0,)*(k - len(i)) + i
  2746. def prime_as_sum_of_two_squares(p):
  2747. """
  2748. Represent a prime `p` as a unique sum of two squares; this can
  2749. only be done if the prime is congruent to 1 mod 4.
  2750. Examples
  2751. ========
  2752. >>> from sympy.solvers.diophantine.diophantine import prime_as_sum_of_two_squares
  2753. >>> prime_as_sum_of_two_squares(7) # can't be done
  2754. >>> prime_as_sum_of_two_squares(5)
  2755. (1, 2)
  2756. Reference
  2757. =========
  2758. .. [1] Representing a number as a sum of four squares, [online],
  2759. Available: https://schorn.ch/lagrange.html
  2760. See Also
  2761. ========
  2762. sum_of_squares()
  2763. """
  2764. if not p % 4 == 1:
  2765. return
  2766. if p % 8 == 5:
  2767. b = 2
  2768. else:
  2769. b = 3
  2770. while pow(b, (p - 1) // 2, p) == 1:
  2771. b = nextprime(b)
  2772. b = pow(b, (p - 1) // 4, p)
  2773. a = p
  2774. while b**2 > p:
  2775. a, b = b, a % b
  2776. return (int(a % b), int(b)) # convert from long
  2777. def sum_of_three_squares(n):
  2778. r"""
  2779. Returns a 3-tuple $(a, b, c)$ such that $a^2 + b^2 + c^2 = n$ and
  2780. $a, b, c \geq 0$.
  2781. Returns None if $n = 4^a(8m + 7)$ for some `a, m \in \mathbb{Z}`. See
  2782. [1]_ for more details.
  2783. Usage
  2784. =====
  2785. ``sum_of_three_squares(n)``: Here ``n`` is a non-negative integer.
  2786. Examples
  2787. ========
  2788. >>> from sympy.solvers.diophantine.diophantine import sum_of_three_squares
  2789. >>> sum_of_three_squares(44542)
  2790. (18, 37, 207)
  2791. References
  2792. ==========
  2793. .. [1] Representing a number as a sum of three squares, [online],
  2794. Available: https://schorn.ch/lagrange.html
  2795. See Also
  2796. ========
  2797. sum_of_squares()
  2798. """
  2799. special = {1:(1, 0, 0), 2:(1, 1, 0), 3:(1, 1, 1), 10: (1, 3, 0), 34: (3, 3, 4), 58:(3, 7, 0),
  2800. 85:(6, 7, 0), 130:(3, 11, 0), 214:(3, 6, 13), 226:(8, 9, 9), 370:(8, 9, 15),
  2801. 526:(6, 7, 21), 706:(15, 15, 16), 730:(1, 27, 0), 1414:(6, 17, 33), 1906:(13, 21, 36),
  2802. 2986: (21, 32, 39), 9634: (56, 57, 57)}
  2803. v = 0
  2804. if n == 0:
  2805. return (0, 0, 0)
  2806. v = multiplicity(4, n)
  2807. n //= 4**v
  2808. if n % 8 == 7:
  2809. return
  2810. if n in special.keys():
  2811. x, y, z = special[n]
  2812. return _sorted_tuple(2**v*x, 2**v*y, 2**v*z)
  2813. s, _exact = integer_nthroot(n, 2)
  2814. if _exact:
  2815. return (2**v*s, 0, 0)
  2816. x = None
  2817. if n % 8 == 3:
  2818. s = s if _odd(s) else s - 1
  2819. for x in range(s, -1, -2):
  2820. N = (n - x**2) // 2
  2821. if isprime(N):
  2822. y, z = prime_as_sum_of_two_squares(N)
  2823. return _sorted_tuple(2**v*x, 2**v*(y + z), 2**v*abs(y - z))
  2824. return
  2825. if n % 8 in (2, 6):
  2826. s = s if _odd(s) else s - 1
  2827. else:
  2828. s = s - 1 if _odd(s) else s
  2829. for x in range(s, -1, -2):
  2830. N = n - x**2
  2831. if isprime(N):
  2832. y, z = prime_as_sum_of_two_squares(N)
  2833. return _sorted_tuple(2**v*x, 2**v*y, 2**v*z)
  2834. def sum_of_four_squares(n):
  2835. r"""
  2836. Returns a 4-tuple `(a, b, c, d)` such that `a^2 + b^2 + c^2 + d^2 = n`.
  2837. Here `a, b, c, d \geq 0`.
  2838. Usage
  2839. =====
  2840. ``sum_of_four_squares(n)``: Here ``n`` is a non-negative integer.
  2841. Examples
  2842. ========
  2843. >>> from sympy.solvers.diophantine.diophantine import sum_of_four_squares
  2844. >>> sum_of_four_squares(3456)
  2845. (8, 8, 32, 48)
  2846. >>> sum_of_four_squares(1294585930293)
  2847. (0, 1234, 2161, 1137796)
  2848. References
  2849. ==========
  2850. .. [1] Representing a number as a sum of four squares, [online],
  2851. Available: https://schorn.ch/lagrange.html
  2852. See Also
  2853. ========
  2854. sum_of_squares()
  2855. """
  2856. if n == 0:
  2857. return (0, 0, 0, 0)
  2858. v = multiplicity(4, n)
  2859. n //= 4**v
  2860. if n % 8 == 7:
  2861. d = 2
  2862. n = n - 4
  2863. elif n % 8 in (2, 6):
  2864. d = 1
  2865. n = n - 1
  2866. else:
  2867. d = 0
  2868. x, y, z = sum_of_three_squares(n)
  2869. return _sorted_tuple(2**v*d, 2**v*x, 2**v*y, 2**v*z)
  2870. def power_representation(n, p, k, zeros=False):
  2871. r"""
  2872. Returns a generator for finding k-tuples of integers,
  2873. `(n_{1}, n_{2}, . . . n_{k})`, such that
  2874. `n = n_{1}^p + n_{2}^p + . . . n_{k}^p`.
  2875. Usage
  2876. =====
  2877. ``power_representation(n, p, k, zeros)``: Represent non-negative number
  2878. ``n`` as a sum of ``k`` ``p``\ th powers. If ``zeros`` is true, then the
  2879. solutions is allowed to contain zeros.
  2880. Examples
  2881. ========
  2882. >>> from sympy.solvers.diophantine.diophantine import power_representation
  2883. Represent 1729 as a sum of two cubes:
  2884. >>> f = power_representation(1729, 3, 2)
  2885. >>> next(f)
  2886. (9, 10)
  2887. >>> next(f)
  2888. (1, 12)
  2889. If the flag `zeros` is True, the solution may contain tuples with
  2890. zeros; any such solutions will be generated after the solutions
  2891. without zeros:
  2892. >>> list(power_representation(125, 2, 3, zeros=True))
  2893. [(5, 6, 8), (3, 4, 10), (0, 5, 10), (0, 2, 11)]
  2894. For even `p` the `permute_sign` function can be used to get all
  2895. signed values:
  2896. >>> from sympy.utilities.iterables import permute_signs
  2897. >>> list(permute_signs((1, 12)))
  2898. [(1, 12), (-1, 12), (1, -12), (-1, -12)]
  2899. All possible signed permutations can also be obtained:
  2900. >>> from sympy.utilities.iterables import signed_permutations
  2901. >>> list(signed_permutations((1, 12)))
  2902. [(1, 12), (-1, 12), (1, -12), (-1, -12), (12, 1), (-12, 1), (12, -1), (-12, -1)]
  2903. """
  2904. n, p, k = [as_int(i) for i in (n, p, k)]
  2905. if n < 0:
  2906. if p % 2:
  2907. for t in power_representation(-n, p, k, zeros):
  2908. yield tuple(-i for i in t)
  2909. return
  2910. if p < 1 or k < 1:
  2911. raise ValueError(filldedent('''
  2912. Expecting positive integers for `(p, k)`, but got `(%s, %s)`'''
  2913. % (p, k)))
  2914. if n == 0:
  2915. if zeros:
  2916. yield (0,)*k
  2917. return
  2918. if k == 1:
  2919. if p == 1:
  2920. yield (n,)
  2921. else:
  2922. be = perfect_power(n)
  2923. if be:
  2924. b, e = be
  2925. d, r = divmod(e, p)
  2926. if not r:
  2927. yield (b**d,)
  2928. return
  2929. if p == 1:
  2930. for t in partition(n, k, zeros=zeros):
  2931. yield t
  2932. return
  2933. if p == 2:
  2934. feasible = _can_do_sum_of_squares(n, k)
  2935. if not feasible:
  2936. return
  2937. if not zeros and n > 33 and k >= 5 and k <= n and n - k in (
  2938. 13, 10, 7, 5, 4, 2, 1):
  2939. '''Todd G. Will, "When Is n^2 a Sum of k Squares?", [online].
  2940. Available: https://www.maa.org/sites/default/files/Will-MMz-201037918.pdf'''
  2941. return
  2942. if feasible is not True: # it's prime and k == 2
  2943. yield prime_as_sum_of_two_squares(n)
  2944. return
  2945. if k == 2 and p > 2:
  2946. be = perfect_power(n)
  2947. if be and be[1] % p == 0:
  2948. return # Fermat: a**n + b**n = c**n has no solution for n > 2
  2949. if n >= k:
  2950. a = integer_nthroot(n - (k - 1), p)[0]
  2951. for t in pow_rep_recursive(a, k, n, [], p):
  2952. yield tuple(reversed(t))
  2953. if zeros:
  2954. a = integer_nthroot(n, p)[0]
  2955. for i in range(1, k):
  2956. for t in pow_rep_recursive(a, i, n, [], p):
  2957. yield tuple(reversed(t + (0,)*(k - i)))
  2958. sum_of_powers = power_representation
  2959. def pow_rep_recursive(n_i, k, n_remaining, terms, p):
  2960. # Invalid arguments
  2961. if n_i <= 0 or k <= 0:
  2962. return
  2963. # No solutions may exist
  2964. if n_remaining < k:
  2965. return
  2966. if k * pow(n_i, p) < n_remaining:
  2967. return
  2968. if k == 0 and n_remaining == 0:
  2969. yield tuple(terms)
  2970. elif k == 1:
  2971. # next_term^p must equal to n_remaining
  2972. next_term, exact = integer_nthroot(n_remaining, p)
  2973. if exact and next_term <= n_i:
  2974. yield tuple(terms + [next_term])
  2975. return
  2976. else:
  2977. # TODO: Fall back to diop_DN when k = 2
  2978. if n_i >= 1 and k > 0:
  2979. for next_term in range(1, n_i + 1):
  2980. residual = n_remaining - pow(next_term, p)
  2981. if residual < 0:
  2982. break
  2983. yield from pow_rep_recursive(next_term, k - 1, residual, terms + [next_term], p)
  2984. def sum_of_squares(n, k, zeros=False):
  2985. """Return a generator that yields the k-tuples of nonnegative
  2986. values, the squares of which sum to n. If zeros is False (default)
  2987. then the solution will not contain zeros. The nonnegative
  2988. elements of a tuple are sorted.
  2989. * If k == 1 and n is square, (n,) is returned.
  2990. * If k == 2 then n can only be written as a sum of squares if
  2991. every prime in the factorization of n that has the form
  2992. 4*k + 3 has an even multiplicity. If n is prime then
  2993. it can only be written as a sum of two squares if it is
  2994. in the form 4*k + 1.
  2995. * if k == 3 then n can be written as a sum of squares if it does
  2996. not have the form 4**m*(8*k + 7).
  2997. * all integers can be written as the sum of 4 squares.
  2998. * if k > 4 then n can be partitioned and each partition can
  2999. be written as a sum of 4 squares; if n is not evenly divisible
  3000. by 4 then n can be written as a sum of squares only if the
  3001. an additional partition can be written as sum of squares.
  3002. For example, if k = 6 then n is partitioned into two parts,
  3003. the first being written as a sum of 4 squares and the second
  3004. being written as a sum of 2 squares -- which can only be
  3005. done if the condition above for k = 2 can be met, so this will
  3006. automatically reject certain partitions of n.
  3007. Examples
  3008. ========
  3009. >>> from sympy.solvers.diophantine.diophantine import sum_of_squares
  3010. >>> list(sum_of_squares(25, 2))
  3011. [(3, 4)]
  3012. >>> list(sum_of_squares(25, 2, True))
  3013. [(3, 4), (0, 5)]
  3014. >>> list(sum_of_squares(25, 4))
  3015. [(1, 2, 2, 4)]
  3016. See Also
  3017. ========
  3018. sympy.utilities.iterables.signed_permutations
  3019. """
  3020. yield from power_representation(n, 2, k, zeros)
  3021. def _can_do_sum_of_squares(n, k):
  3022. """Return True if n can be written as the sum of k squares,
  3023. False if it cannot, or 1 if ``k == 2`` and ``n`` is prime (in which
  3024. case it *can* be written as a sum of two squares). A False
  3025. is returned only if it cannot be written as ``k``-squares, even
  3026. if 0s are allowed.
  3027. """
  3028. if k < 1:
  3029. return False
  3030. if n < 0:
  3031. return False
  3032. if n == 0:
  3033. return True
  3034. if k == 1:
  3035. return is_square(n)
  3036. if k == 2:
  3037. if n in (1, 2):
  3038. return True
  3039. if isprime(n):
  3040. if n % 4 == 1:
  3041. return 1 # signal that it was prime
  3042. return False
  3043. else:
  3044. f = factorint(n)
  3045. for p, m in f.items():
  3046. # we can proceed iff no prime factor in the form 4*k + 3
  3047. # has an odd multiplicity
  3048. if (p % 4 == 3) and m % 2:
  3049. return False
  3050. return True
  3051. if k == 3:
  3052. if (n//4**multiplicity(4, n)) % 8 == 7:
  3053. return False
  3054. # every number can be written as a sum of 4 squares; for k > 4 partitions
  3055. # can be 0
  3056. return True