code: purgatorio

ref: 27ca2618d4bc7642370feb39a83a3cff92495ca2
dir: /appl/math/powers.b/

View raw version
implement Powers;

include "sys.m";
	sys: Sys;
include "draw.m";
include "arg.m";
include "lock.m";
	lockm: Lock;
	Semaphore: import lockm;

Powers: module
{
	init: fn(nil: ref Draw->Context, nil: list of string);
};

MAXNODES: con (1<<20)/4;

verbose: int;

# Doing
# 	powers -p 3
# gives
# 	[2] 1729 = 1**3 + 12**3 = 9**3 + 10**3
# 	[2] 4104 = 2**3 + 16**3 = 9**3 + 15**3

# ie 1729 can be written in two ways as the sum of 2 cubes as can 4104.

# The options are

# -p	the power to use - default 2
# -n	the number of powers summed - default 2
# -f	the minimum number of ways found before reporting it - default 2
# -l	the least number to consider - default 0
# -m	the greatest number to consider - default 8192

# Thus
# 	pow -p 4 -n 3 -f 3 -l 0 -m 1000000
# gives
# 	[3] 811538 = 12**4 + 17**4 + 29**4 = 7**4 + 21**4 + 28**4 = 4**4 + 23**4 + 27**4

# ie fourth powers, 3 in each sum, minimum of 3 representations, numbers from 0-1000000.

# [2] 25
# [3] 325
# [4] 1105
# [5] 4225
# [6] 5525
# [7] 203125
# [8] 27625
# [9] 71825
# [10] 138125
# [11] 2640625
# [12] 160225
# [13] 17850625
# [14] 1221025
# [15] 1795625
# [16] 801125
# [18] 2082925
# [20] 4005625
# [23] 30525625
# [24] 5928325
# [32] 29641625

# [24] 5928325 = 63**2 + 2434**2 = 94**2 + 2433**2 = 207**2 + 2426**2 = 294**2 + 2417**2 = 310**2 + 2415**2 = 465**2 + 2390**2 = 490**2 + 2385**2 = 591**2 + 2362**2 = 690**2 + 2335**2 = 742**2 + 2319**2 = 849**2 + 2282**2 = 878**2 + 2271**2 = 959**2 + 2238**2 = 1039**2 + 2202**2 = 1062**2 + 2191**2 = 1201**2 + 2118**2 = 1215**2 + 2110**2 = 1290**2 + 2065**2 = 1410**2 + 1985**2 = 1454**2 + 1953**2 = 1535**2 + 1890**2 = 1614**2 + 1823**2 = 1633**2 + 1806**2 = 1697**2 + 1746**2

# [32] 29641625 = 67**2 + 5444**2 = 124**2 + 5443**2 = 284**2 + 5437**2 = 320**2 + 5435**2 = 515**2 + 5420**2 = 584**2 + 5413**2 = 835**2 + 5380**2 = 955**2 + 5360**2 = 1180**2 + 5315**2 = 1405**2 + 5260**2 = 1460**2 + 5245**2 = 1648**2 + 5189**2 = 1795**2 + 5140**2 = 1829**2 + 5128**2 = 1979**2 + 5072**2 = 2012**2 + 5059**2 = 2032**2 + 5051**2 = 2245**2 + 4960**2 = 2308**2 + 4931**2 = 2452**2 + 4861**2 = 2560**2 + 4805**2 = 2621**2 + 4772**2 = 2840**2 + 4645**2 = 3005**2 + 4540**2 = 3035**2 + 4520**2 = 3320**2 + 4315**2 = 3365**2 + 4280**2 = 3517**2 + 4156**2 = 3544**2 + 4133**2 = 3664**2 + 4027**2 = 3715**2 + 3980**2 = 3803**2 + 3896**2

# [2] 1729 = 1**3 + 12**3 = 9**3 + 10**3
# [2] 4104 = 2**3 + 16**3 = 9**3 + 15**3
# [3] 87539319 = 167**3 + 436**3 = 228**3 + 423**3 = 255**3 + 414**3

# [2] 635318657 = 59**4 + 158**4 = 133**4 + 134**4
# [2] 3262811042 = 7**4 + 239**4 = 157**4 + 227**4
# [2] 8657437697 = 193**4 + 292**4 = 256**4 + 257**4
# [2] 68899596497 = 271**4 + 502**4 = 298**4 + 497**4
# [2] 86409838577 = 103**4 + 542**4 = 359**4 + 514**4
# [2] 160961094577 = 222**4 + 631**4 = 503**4 + 558**4
# [2] 2094447251857 = 76**4 + 1203**4 = 653**4 + 1176**4
# [2] 4231525221377 = 878**4 + 1381**4 = 997**4 + 1342**4
# [2] 26033514998417 = 1324**4 + 2189**4 = 1784**4 + 1997**4
# [2] 37860330087137 = 1042**4 + 2461**4 = 2026**4 + 2141**4
# [2] 61206381799697 = 248**4 + 2797**4 = 2131**4 + 2524**4
# [2] 76773963505537 = 1034**4 + 2949**4 = 1797**4 + 2854**4
# [2] 109737827061041 = 1577**4 + 3190**4 = 2345**4 + 2986**4
# [2] 155974778565937 = 1623**4 + 3494**4 = 2338**4 + 3351**4
# [2] 156700232476402 = 661**4 + 3537**4 = 2767**4 + 3147**4
# [2] 621194785437217 = 2694**4 + 4883**4 = 3966**4 + 4397**4
# [2] 652057426144337 = 604**4 + 5053**4 = 1283**4 + 5048**4
# [2] 680914892583617 = 3364**4 + 4849**4 = 4288**4 + 4303**4
# [2] 1438141494155441 = 2027**4 + 6140**4 = 4840**4 + 5461**4
# [2] 1919423464573697 = 274**4 + 6619**4 = 5093**4 + 5942**4
# [2] 2089568089060657 = 498**4 + 6761**4 = 5222**4 + 6057**4
# [2] 2105144161376801 = 2707**4 + 6730**4 = 3070**4 + 6701**4
# [2] 3263864585622562 = 1259**4 + 7557**4 = 4661**4 + 7269**4
# [2] 4063780581008977 = 5181**4 + 7604**4 = 6336**4 + 7037**4
# [2] 6315669699408737 = 1657**4 + 8912**4 = 7432**4 + 7559**4
# [2] 6884827518602786 = 635**4 + 9109**4 = 3391**4 + 9065**4
# [2] 7191538859126257 = 4903**4 + 9018**4 = 6842**4 + 8409**4
# [2] 7331928977565937 = 1104**4 + 9253**4 = 5403**4 + 8972**4
# [2] 7362748995747617 = 5098**4 + 9043**4 = 6742**4 + 8531**4
# [2] 7446891977980337 = 1142**4 + 9289**4 = 4946**4 + 9097**4
# [2] 7532132844821777 = 173**4 + 9316**4 = 4408**4 + 9197**4
# [2] 7985644522300177 = 6262**4 + 8961**4 = 7234**4 + 8511**4

# 5, 6, 7, 8, 9, 10, 11 none

Btree: adt{
	sum: big;
	left: cyclic ref Btree;
	right: cyclic ref Btree;
};

Dtree: adt{
	sum: big;
	freq: int;
	lst: list of array of int;
	left: cyclic ref Dtree;
	right: cyclic ref Dtree;
};

nCr(n: int, r: int): int
{
	if(r > n-r)
		r = n-r;

	# f := g := 1;
	# for(i := 0; i < r; i++){
	# 	f *= n-i;
	# 	g *= i+1;
	# }
	# return f/g;

	num := array[r] of int;
	den := array[r] of int;
	for(i := 0; i < r; i++){
		num[i] = n-i;
		den[i] = i+1;
	}
	for(i = 0; i < r; i++){
		for(j := 0; den[i] != 1; j++){
			if(num[j] == 1)
				continue;
			k := hcf(num[j], den[i]);
			if(k != 1){
				num[j] /= k;
				den[i] /= k;
			}
		}
	}
	f := 1;
	for(i = 0; i < r; i++)
		f *= num[i];
	return f;
}

nHr(n: int, r: int): int
{
	if(n == 0)
		return 0;
	return nCr(n+r-1, r);
}

nSr(n: int, i: int, j: int): int
{
	return nHr(j, n)-nHr(i, n);
	# s := 0;
	# for(k := i; k < j; k++)
	# 	s += nHr(k+1, n-1);
	# return s;
}

nSrmax(n: int, i: int, m: int): int
{
	s := 0;
	for(k := i; ; k++){
		s += nHr(k+1, n-1);
		if(s > m)
			break;
	}
	if(k == i)
		return i+1;
	return k;
}

kth(c: array of int, n: int, i: int, j: int, k: int)
{
	l, u: int;

	m := nSr(n, i, j);
	if(k < 0)
		k = 0;
	if(k >= m)
		k = m-1;
	p := 0;
	for(q := 0; q < n; q++){
		if(q == 0){
			l = i;
			u = j-1;
		}
		else{
			l = 0;
			u = c[q-1];
		}
		for(x := l; x <= u; x++){
			m = nHr(x+1, n-q-1);
			p += m;
			if(p > k){
				p -= m;
				break;
			}
		}
		c[q] = x;
	}	
}

pos(c: array of int, n: int): int
{
	p := 0;
	for(q := 0; q < n; q++)
		p += nSr(n-q, 0, c[q]);
	return p;
}

min(c: array of int, n: int, p: int): big
{
	s := big(0);
	for(i := 0; i < n; i++)
		s += big(c[i])**p;
	m := s;
	for(i = n-1; i > 0; i--){
		s -= big(c[i])**p;
		s -= big(c[i-1])**p;
		c[i]--;
		c[i-1]++;
		s += big(c[i-1])**p;
		if(s < m)
			m = s;
	}
	c[0]--;
	c[n-1]++;
	# m--;
	return m;
}

hcf(a, b: int): int
{
	if(b == 0)
		return a;
	for(;;){
		if(a == 0)
			break;
		if(a < b)
			(a, b) = (b, a);
		a %= b;
		# a -= (a/b)*b;
	}
	return b;
}

gcd(l: list of array of int): int
{
	g := (hd l)[0];
	for(; l != nil; l = tl l){
		d := hd l;
		n := len d;
		for(i := 0; i < n; i++)
			g = hcf(d[i], g);
	}
	return g;
}

adddup(s: big, root: ref Dtree): int
{
	n, p, lp: ref Dtree;
	
	p = root;
	while(p != nil){
		if(s == p.sum)
			return ++p.freq;
		lp = p;
		if(s < p.sum)
			p = p.left;
		else
			p = p.right;
	}
	n = ref Dtree(s, 2, nil, nil, nil);
	if(s < lp.sum)
		lp.left = n;
	else
		lp.right = n;
	return n.freq;
}

cp(c: array of int): array of int
{
	n := len c;
	m := 0;
	for(i := 0; i < n; i++)
		if(c[i] != 0)
			m++;
	nc := array[m] of int;
	nc[0: ] = c[0: m];
	return nc;
}

finddup(s: big, c: array of int, root: ref Dtree, f: int)
{
	p: ref Dtree;
	
	p = root;
	while(p != nil){
		if(s == p.sum){
			if(p.freq >= f)
				p.lst = cp(c) :: p.lst;
			return;
		}
		if(s < p.sum)
			p = p.left;
		else
			p = p.right;
	}
}

printdup(p: ref Dtree, pow: int, ix: int)
{
	if(p == nil)
		return;
	printdup(p.left, pow, ix);
	if((l := p.lst) != nil){
		if(gcd(l) == 1){
			min1 := min2 := 16r7fffffff;
			for(; l != nil; l = tl l){
				n := len hd l;
				if(n < min1){
					min2 = min1;
					min1 = n;
				}
				else if(n < min2)
					min2 = n;
			}
			i := min1+min2-pow;
			if(i <= ix){
				sys->print("[%d, %d] %bd", i, p.freq, p.sum);
				for(l = p.lst; l != nil; l = tl l){
					d := hd l;
					n := len d;
					sys->print(" = ");
					for(j := n-1; j >= 0; j--){
						sys->print("%d**%d", d[j], pow);
						if(j > 0)
							sys->print(" + ");
					}
				}
				sys->print("\n");
				if(i < 0){
					sys->print("****************\n");
					exit;
				}
			}
		}
	}
	printdup(p.right, pow, ix);
}

addsum(s: big, root: ref Btree, root1: ref Dtree): int
{
	n, p, lp: ref Btree;
	
	p = root;
	while(p != nil){
		if(s == p.sum)
			return adddup(s, root1);
		lp = p;
		if(s < p.sum)
			p = p.left;
		else
			p = p.right;
	}
	n = ref Btree(s, nil, nil);
	if(s < lp.sum)
		lp.left = n;
	else
		lp.right = n;
	return 1;
}

oiroot(x: big, p: int): int
{
	for(i := 0; ; i++){
		n := big(i)**p;
		if(n > x)
			break;
	}
	return i-1;
}

iroot(x: big, p: int): int
{
	m: big;

	if(x == big(0) || x == big(1))
		return int x;
	v := x;
	n := 0;
	for(i := 32; i > 0; i >>= 1){
		m = ((big(1)<<i)-big(1))<<i;
		if((v&m) != big(0)){
			n += i;
			v >>= i;
		}
	}
	a := big(1) << (n/p);
	b := a<<1;
	while(a < b){
		m = (a+b+big(1))/big(2);
		y := m**p;
		if(y > x)
			b = m-big(1);
		else if(y < x)
			a = m;
		else
			a = b = m;
	}
	if(a**p <= x && (a+big(1))**p > x)
		;
	else{
		sys->print("fatal: %bd %d -> %bd\n", x, p, a);
		exit;
	}
	return int a;
}

initval(c: array of int, n: int, p: int, v: int): big
{
	for(i := 0; i < n; i++)
		c[i] = 0;
	c[0] = v;
	return big(v)**p;
}

nxtval(c: array of int, n: int, p: int, s: big): big
{
	for(k := n-1; k >= 0; k--){
		s -= big(c[k])**p;
		c[k]++;
		if(k == 0){
			s += big(c[k])**p;
			break;
		}
		else{
			if(c[k] <= c[k-1]){
				s += big(c[k])**p;
				break;
			}
			c[k] = 0;
		}
	}
	return s;
}

powers(p: int, n: int, f: int, ix: int, lim0: big, lim: big, ch: chan of int, lock: ref Semaphore)
{
	root := ref Btree(big(-1), nil, nil);
	root1 := ref Dtree(big(-1), 0, nil, nil, nil);

	min := max := lim0;

	c := array[n] of int;

	for(;;){
		imin := iroot((min+big(n-1))/big(n), p);
		imax := nSrmax(n, imin, MAXNODES);
		max = big(imax)**p - big(1);
		while(max <= min){	# could do better
			imax++;
			max = big(imax)**p - big(1);
		}
		if(max > lim){
			max = lim;
			imax = iroot(max, p)+1;
		}

		if(verbose)
			sys->print("searching in %d-%d(%bd-%bd)\n", imin, imax, min, max);

		m := mm := 0;
		maxf := 0;
		s := initval(c, n, p, imin);
		for(;;){
			mm++;
			if(s >= min && s < max){
				fr := addsum(s, root, root1);
				if(fr > maxf)
					maxf = fr;
				m++;
			}
			s = nxtval(c, n, p, s);
			if(c[0] == imax)
				break;
		}

		root.left = root.right = nil;

		if(maxf >= f){
			if(verbose)
				sys->print("finding duplicates\n");

			s = initval(c, n, p, imin);
			for(;;){
				if(s >= min && s < max)
					finddup(s, c, root1, f);
				s = nxtval(c, n, p, s);
				if(c[0] == imax)
					break;
			}

			if(lock != nil)
				lock.obtain();
			printdup(root1, p, ix);
			if(lock != nil)
				lock.release();

			root1.left = root1.right = nil;
		}

		if(verbose)
			sys->print("%d(%d) nodes searched\n", m, mm);

		if(mm != nSr(n, imin, imax)){
			sys->print("**fatal**\n");
			exit;
		}

		min = max;
		if(min >= lim)
			break;
	}
	if(ch != nil)
		ch <-= 0;
}

usage()
{
	sys->print("usage: powers -p power -n number -f frequency -i index -l minimum -m maximum -s procs -v\n");
	exit;
}

partition(p: int, n: int, l: big, m: big, s: int): array of big
{
	a := array[s+1] of big;
	a[0] = big(iroot(l, p))**n;
	a[s] = (big(iroot(m, p))+big(1))**n;
	nn := a[s]-a[0];
	q := nn/big(s);
	r := nn-q*big(s);
	t := big(0);
	lb := a[0];
	for(i := 0; i < s; i++){
		ub := lb+q;
		t += r;
		if(t >= big(s)){
			ub++;
			t -= big(s);
		}
		a[i+1] = ub;
		lb = ub;
	}
	if(a[s] != a[0]+nn){
		sys->print("fatal: a[s]\n");
		exit;
	}
	for(i = 0; i < s; i++){
		# sys->print("%bd %bd\n", a[i], a[i]**p);
		a[i] = big(iroot(a[i], n))**p;
	}
	a[0] = l;
	a[s] = m;
	while(a[0] >= a[1]){
		a[1] = a[0];
		a = a[1: ];
		--s;
	}
	while(a[s] <= a[s-1]){
		a[s-1] = a[s];
		a = a[0: s];
		--s;
	}
	return a;
}

init(nil: ref Draw->Context, args: list of string)
{
	sys = load Sys Sys->PATH;
	arg := load Arg Arg->PATH;
	lockm = load Lock Lock->PATH;

	lockm->init();
	lock := Semaphore.new();

	p := n := f := 2;
	ix := 1<<30;
	l := m := big(0);
	s := 1;

	arg->init(args);
	while((c := arg->opt()) != 0){
		case c {
			'p' =>
				p = int arg->arg();
			'n' =>
				n = int arg->arg();
			'f' =>
				f = int arg->arg();
			'i' =>
				ix = int arg->arg();
			'l' =>
				l = big(arg->arg());
			'm' =>
				m = big(arg->arg())+big(1);
			's' =>
				s = int arg->arg();
			'v' =>
				verbose = 1;
			* =>
				usage();
		}
	}
	if(arg->argv() != nil)
		usage();

	if(p < 2){
		p = 2;
		sys->print("setting p = %d\n", p);
	}
	if(n < 2){
		n = 2;
		sys->print("setting n = %d\n", n);
	}
	if(f < 2){
		f = 2;
		sys->print("setting f = %d\n", f);
	}
	if(l < big(0)){
		l = big(0);
		sys->print("setting l = %bd\n", l);
	}
	if(m <= big(0)){
		m = big((1<<13)+1);
		sys->print("setting m = %bd\n", m-big(1));
	}
	if(l >= m)
		exit;

	if(s <= 1)
		powers(p, n, f, ix, l, m, nil, nil);
	else{
		nproc := 0;
		ch := chan of int;
		a := partition(p, n, l, m, s);
		lb := a[0];
		for(i := 0; i < s; i++){
			ub := a[i+1];
			if(lb < ub){
				nproc++;
				spawn powers(p, n, f, ix, lb, ub, ch, lock);
			}
			lb = ub;
		}
		for( ; nproc != 0; nproc--)
			<- ch;
	}
}