Task: Addition-chain exponentiation

C: #include <stdio.h>

#include "achain.c" 


typedef struct {double u, v;} cplx;

inline cplx c_mul(cplx a, cplx b)
{
	cplx c;
	c.u = a.u * b.u - a.v * b.v;
	c.v = a.u * b.v + a.v * b.u;
	return c;
}

cplx chain_expo(cplx x, int n)
{
	int i, j, k, l, e[32];
	cplx v[32];

	l = seq(n, 0, e);

	puts("Exponents:");
	for (i = 0; i <= l; i++)
		printf("%d%c", e[i], i == l ? '\n' : ' ');

	v[0] = x; v[1] = c_mul(x, x);
	for (i = 2; i <= l; i++) {
		for (j = i - 1; j; j--) {
			for (k = j; k >= 0; k--) {
				if (e[k] + e[j] < e[i]) break;
				if (e[k] + e[j] > e[i]) continue;
				v[i] = c_mul(v[j], v[k]);
				j = 1;
				break;
			}
		}
	}
	printf("(%f + i%f)^%d = %f + i%f\n",
		x.u, x.v, n, v[l].u, v[l].v);

	return x;
}

int bin_len(int n)
{
	int r, o;
	for (r = o = -1; n; n >>= 1, r++)
		if (n & 1) o++;
	return r + o;
}

int main()
{
	cplx	r1 = {1.0000254989, 0.0000577896},
		r2 = {1.0000220632, 0.0000500026};
	int n1 = 27182, n2 = 31415, i;

	init();
	puts("Precompute chain lengths");
	seq_len(n2);

	chain_expo(r1, n1);
	chain_expo(r2, n2);
	puts("\nchain lengths: shortest binary");
	printf("%14d %7d %7d\n", n1, seq_len(n1), bin_len(n1));
	printf("%14d %7d %7d\n", n2, seq_len(n2), bin_len(n2));
	for (i = 1; i < 100; i++)
		printf("%14d %7d %7d\n", i, seq_len(i), bin_len(i));
	return 0;
}







Python: 

from math import sqrt
from numpy import array
from mpmath import mpf


class AdditionChains:
    

    def __init__(self):
        
        self.chains, self.idx, self.pos = [[1]], 0, 0
        self.pat, self.lvl = {1: 0}, [[1]]

    def add_chain(self):
        
        newchain = self.chains[self.idx].copy()
        newchain.append(self.chains[self.idx][-1] +
                        self.chains[self.idx][self.pos])
        self.chains.append(newchain)
        if self.pos == len(self.chains[self.idx])-1:
            self.idx += 1
            self.pos = 0
        else:
            self.pos += 1
        return newchain

    def find_chain(self, nexp):
        
        assert nexp > 0
        if nexp == 1:
            return [1]
        chn = next((a for a in self.chains if a[-1] == nexp), None)
        if chn is None:
            while True:
                chn = self.add_chain()
                if chn[-1] == nexp:
                    break

        return chn

    def knuth_path(self, ngoal):
        
        if ngoal < 1:
            return []
        while not ngoal in self.pat:
            new_lvl = []
            for i in self.lvl[0]:
                for j in self.knuth_path(i):
                    if not i + j in self.pat:
                        self.pat[i + j] = i
                        new_lvl.append(i + j)

            self.lvl[0] = new_lvl

        returnpath = self.knuth_path(self.pat[ngoal])
        returnpath.append(ngoal)
        return returnpath


def cpow(xbase, chain):
    
    pows, products = 0, {0: 1, 1: xbase}
    for i in chain:
        products[i] = products[pows] * products[i - pows]
        pows = i
    return products[chain[-1]]


if __name__ == '__main__':
    
    acs = AdditionChains()
    print('First one hundred addition chain lengths:')
    for k in range(1, 101):
        print(f'{len(acs.find_chain(k))-1:3}', end='\n'if k % 10 == 0 else '')

    print('\nKnuth chains for addition chains of 31415 and 27182:')
    chns = {m: acs.knuth_path(m) for m in [31415, 27182]}
    for (num, cha) in chns.items():
        print(f'Exponent: {num:10}\n  Addition Chain: {cha[:-1]}')
    print('\n1.00002206445416^31415 =', cpow(1.00002206445416, chns[31415]))
    print('1.00002550055251^27182 =', cpow(1.00002550055251, chns[27182]))
    print('1.000025 + 0.000058i)^27182 =',
          cpow(complex(1.000025, 0.000058), chns[27182]))
    print('1.000022 + 0.000050i)^31415 =',
          cpow(complex(1.000022, 0.000050), chns[31415]))
    sq05 = mpf(sqrt(0.5))
    mat = array([[sq05, 0, sq05, 0, 0, 0], [0, sq05, 0, sq05, 0, 0], [0, sq05, 0, -sq05, 0, 0],
                 [-sq05, 0, sq05, 0, 0, 0], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 1, 0]])
    print('matrix A ^ 27182 =')
    print(cpow(mat, chns[27182]))
    print('matrix A ^ 31415 =')
    print(cpow(mat, chns[31415]))
    print('(matrix A ** 27182) ** 31415 =')
    print(cpow(cpow(mat, chns[27182]), chns[31415]))