Some code to calculate the Mandelbrot set at arbitrary precision using GMP. It does orbit detection. I release this code under the GNU Public License.
#include <gmp.h>
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
typedef unsigned int Iter;
#define ORBIT
long long total_iterations=0;
long long saved_iterations=0;
void inner_loop(int bits, const mpz_t a, const mpz_t b, const mpz_t threshold, Iter max_iter,
Iter *iterations, bool *in_m_set) {
//NOTE that a,b is expected to be premultiplied by 2^bits, and threshold is 4*4^bits;
mpz_t x2,y2,add;
mpz_init(x2);
mpz_init(y2);
mpz_init(add);
mpz_t xfast,yfast;
mpz_init_set(xfast,a);
mpz_init_set(yfast,b);
//fast starts with an advantage already
#ifdef ORBIT
mpz_t xslow,yslow;
mpz_init_set_si(xslow,0);
mpz_init_set_si(yslow,0);
#endif
*in_m_set=true;
for(*iterations=0;*iterations<max_iter;++*iterations){
mpz_pow_ui(x2,xfast,2);
mpz_pow_ui(y2,yfast,2);
mpz_add(add,x2,y2);
if (mpz_cmp(add,threshold)>0) {
*in_m_set=false;
break;
}
mpz_mul(yfast,yfast,xfast);
mpz_tdiv_q_2exp(yfast,yfast,bits-1); //y=2*x*y
mpz_sub(xfast,x2,y2);
mpz_tdiv_q_2exp(xfast,xfast,bits);
#ifdef ORBIT
mpz_pow_ui(x2,xslow,2);
mpz_pow_ui(y2,yslow,2);
mpz_add(add,x2,y2);
mpz_mul(yslow,yslow,xslow);
mpz_tdiv_q_2exp(yslow,yslow,bits-1); //y=2*x*y
mpz_sub(xslow,x2,y2);
mpz_tdiv_q_2exp(xslow,xslow,bits);
if (mpz_cmp(xslow,xfast)==0 && mpz_cmp(yslow,yfast)==0) {
//orbit detection
saved_iterations+= (max_iter-*iterations);
break;
//in_m_set is already true
}
#endif
mpz_add(yfast,yfast,b);
mpz_add(xfast,xfast,a);
++*iterations;
++total_iterations;
mpz_pow_ui(x2,xfast,2);
mpz_pow_ui(y2,yfast,2);
mpz_add(add,x2,y2);
if (mpz_cmp(add,threshold)>0) {
*in_m_set=false;
break;
}
mpz_mul(yfast,yfast,xfast);
mpz_tdiv_q_2exp(yfast,yfast,bits-1); //y=2*x*y
mpz_sub(xfast,x2,y2);
mpz_tdiv_q_2exp(xfast,xfast,bits);
#ifdef ORBIT
if (mpz_cmp(xslow,xfast)==0 && mpz_cmp(yslow,yfast)==0) {
//orbit detection
saved_iterations+= (max_iter-*iterations);
break;
//in_m_set is already true
}
mpz_add(xslow,xslow,a);
mpz_add(yslow,yslow,b);
#endif
mpz_add(yfast,yfast,b);
mpz_add(xfast,xfast,a);
++total_iterations;
}
}
const int pixel_bits=10;
/* two experiments saw convergence around 50. 40 is pushing it. as low as 10 if
you don't need it to be perfect. 60 or 70 to be safe. */
const int screen_half_width=100;
void calc2(Iter max_iter, mpz_t Cx0, mpz_t Cy0, int Cbits, int bits) {
cout << "P5" << endl << 2*screen_half_width+1 << " " << 2*screen_half_width+1 << endl << 255 << endl;
assert(bits>=Cbits);
mpz_t cx,cy;
mpz_init(cx);
mpz_init(cy);
mpz_mul_2exp(cx,Cx0,bits-Cbits);
mpz_mul_2exp(cy,Cy0,bits-Cbits);
mpz_t threshold;
mpz_init_set_si(threshold,4);
mpz_mul_2exp(threshold,threshold,2*bits);
mpz_t a,b;
mpz_init(a);
mpz_init(b);
for(int dx=-screen_half_width;dx<=screen_half_width;++dx){
mpz_set_si(a,dx);
mpz_mul_2exp(a,a,pixel_bits);
mpz_add(a,a,cx);
for(int dy=-screen_half_width;dy<=screen_half_width;++dy){
mpz_set_si(b,dy);
mpz_mul_2exp(b,b,pixel_bits);
mpz_add(b,b,cy);
Iter i;
bool in_m_set;
inner_loop(bits,a,b,threshold,max_iter, &i,&in_m_set);
if (in_m_set) {
putchar(0);
//putchar('M');
}
else {
putchar(1+(i%255));
//putchar(' ');
}
}
//putchar('\n');
}
}
int main(int argc, char**argv){
assert(argc==2);
int mag=atoi(argv[1]);
mpz_t cx,cy;
mpz_init_set_si(cx,-1314468); // -1
mpz_init_set_si(cy,-397404);
calc2(100000,cx,cy,20,mag+pixel_bits);
std::cerr << total_iterations << endl << saved_iterations << endl;
}
No comments :
Post a Comment