package crypto

import (
	
	
	
)

// This piece of software can be found in multiple projects, referenced as "splitPQ":
//	* https://github.com/sdidyk/mtproto/blob/cf2cb57ade6932a7e0854f2e3246492a2028d369/math.go#L44
//	* https://github.com/cjongseok/mtproto/blob/dab4e1a6538c990d8a4450d700559197c344a63b/crypto.go#L48
//	* https://gist.github.com/asaelko/6f27c8ccd2edfab4d1efadf80a5221a4
//	* https://github.com/xelaj/mtproto/blob/13ca2c1b5bfaa249ec925921760be0dc05735915/math.go#L39
//
// Also there is StackOverflow question about this part of telegram auth:
//	* https://stackoverflow.com/questions/31953836/decompose-a-number-into-2-prime-co-factors
//
// General information can be found in telegram auth docs:
//	* https://core.telegram.org/mtproto/auth_key#dh-exchange-initiation
//	* https://core.telegram.org/mtproto/samples-auth_key#4-encrypted-data-generation
//
// Looks like this is some kind of integer factorization algorithm implementation
// which is used as Proof of Work by Telegram Server for some reason.
//
// TODO(ernado): Try https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm

// DecomposePQ decomposes pq into prime factors such that p < q.
func ( *big.Int,  io.Reader) (,  *big.Int,  error) { // nolint:gocognit
	var (
		  = big.NewInt(0)
		  = big.NewInt(1)
		 = big.NewInt(15)
		 = big.NewInt(17)
		  = big.NewInt(0).SetBit(big.NewInt(0), 64, 1)

		        = big.NewInt(0)
		 = big.NewInt(0)

		 = big.NewInt(0)
		 = big.NewInt(0)
		 = big.NewInt(0)

		 = big.NewInt(0)

		 = big.NewInt(0)
	)

	 := big.NewInt(0).Set()
	 := big.NewInt(0)
	 := 0
	for !(.Cmp() == 1 && .Cmp() == -1) {
		,  := rand.Int(, )
		if  != nil {
			return nil, nil, 
		}
		 = .And(, )
		 = .Add(, )
		 = .Mod(, )

		,  := rand.Int(, )
		if  != nil {
			return nil, nil, 
		}
		.Sub(, )
		 = .Mod(, )
		 = .Add(, )

		.Set()
		 := 1 << (uint() + 18)
		 := 1
		 := true

		for  <  &&  {
			.Set()
			.Set()
			.Set()

			for .Cmp() == 1 {
				.SetInt64(0)
				if .And(, ).Cmp() == 1 {
					.Add(, )
					if .Cmp() >= 0 {
						.Sub(, )
					}
				}
				.Add(, )
				if .Cmp() >= 0 {
					.Sub(, )
				}
				.Rsh(, 1)
			}
			.Set()

			.SetInt64(0)
			if .Cmp() == -1 {
				.Add(, )
				.Sub(, )
			} else {
				.Sub(, )
			}
			.GCD(nil, nil, , )

			if ( & ( - 1)) == 0 {
				.Set()
			}
			++

			if .Cmp() != 0 {
				 = false
			}
		}
		++
	}

	 = big.NewInt(0).Set()
	 = big.NewInt(0).Div(, )

	if .Cmp() == 1 {
		,  = , 
	}

	return , , nil
}