Pocitanie PI, vlakna
Created: 2010-12-09 - 08:15
// 1) Create a new windows application with a default name
// 2) Copy from here evrithing (except from these green comments)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
partial class Form1: Form
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.NumericUpDown decimalPlacesNumericUpDown;
private System.Windows.Forms.Button calcButton;
private System.Windows.Forms.TextBox resultsTextBox;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.StatusStrip progressStatusStrip;
private System.Windows.Forms.ToolStripStatusLabel calcToolStripStatusLabel;
private System.Windows.Forms.ToolStripProgressBar calcToolStripProgressBar;
private System.ComponentModel.BackgroundWorker backgroundWorker;
private System.Windows.Forms.ToolStripStatusLabel digitsSoFarToolStripStatusLabel;
public Form1()
{
InitializeComponent();
ini2();
}
void ini2()
{
this.label1 = new System.Windows.Forms.Label();
this.decimalPlacesNumericUpDown = new System.Windows.Forms.NumericUpDown();
this.calcButton = new System.Windows.Forms.Button();
this.resultsTextBox = new System.Windows.Forms.TextBox();
this.panel1 = new System.Windows.Forms.Panel();
this.progressStatusStrip = new System.Windows.Forms.StatusStrip();
this.calcToolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.digitsSoFarToolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.calcToolStripProgressBar = new System.Windows.Forms.ToolStripProgressBar();
this.backgroundWorker = new System.ComponentModel.BackgroundWorker();
((System.ComponentModel.ISupportInitialize)(this.decimalPlacesNumericUpDown)).BeginInit();
this.panel1.SuspendLayout();
this.progressStatusStrip.SuspendLayout();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 12);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(60, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Digits of Pi:";
//
// decimalPlacesNumericUpDown
//
this.decimalPlacesNumericUpDown.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.decimalPlacesNumericUpDown.Location = new System.Drawing.Point(80, 9);
this.decimalPlacesNumericUpDown.Maximum = new decimal(new int[] {
1000001,
0,
0,
0});
this.decimalPlacesNumericUpDown.Name = "decimalPlacesNumericUpDown";
this.decimalPlacesNumericUpDown.Size = new System.Drawing.Size(409, 20);
this.decimalPlacesNumericUpDown.TabIndex = 1;
this.decimalPlacesNumericUpDown.Value = new decimal(new int[] {
500,
0,
0,
0});
//
// calcButton
//
this.calcButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.calcButton.Location = new System.Drawing.Point(495, 7);
this.calcButton.Name = "calcButton";
this.calcButton.Size = new System.Drawing.Size(75, 23);
this.calcButton.TabIndex = 2;
this.calcButton.Text = "Calculate";
this.calcButton.Click += new System.EventHandler(this.calcButton_Click);
//
// resultsTextBox
//
this.resultsTextBox.BackColor = System.Drawing.Color.White;
this.resultsTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.resultsTextBox.Location = new System.Drawing.Point(0, 38);
this.resultsTextBox.Multiline = true;
this.resultsTextBox.Name = "resultsTextBox";
this.resultsTextBox.ReadOnly = true;
this.resultsTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.resultsTextBox.Size = new System.Drawing.Size(583, 263);
this.resultsTextBox.TabIndex = 1;
//
// panel1
//
this.panel1.Controls.Add(this.decimalPlacesNumericUpDown);
this.panel1.Controls.Add(this.label1);
this.panel1.Controls.Add(this.calcButton);
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(583, 38);
this.panel1.TabIndex = 0;
//
// progressStatusStrip
//
this.progressStatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.calcToolStripStatusLabel,
this.digitsSoFarToolStripStatusLabel,
this.calcToolStripProgressBar});
this.progressStatusStrip.Location = new System.Drawing.Point(0, 301);
this.progressStatusStrip.Name = "progressStatusStrip";
this.progressStatusStrip.Size = new System.Drawing.Size(583, 22);
this.progressStatusStrip.TabIndex = 2;
//
// calcToolStripStatusLabel
//
this.calcToolStripStatusLabel.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.calcToolStripStatusLabel.Name = "calcToolStripStatusLabel";
this.calcToolStripStatusLabel.Size = new System.Drawing.Size(568, 17);
this.calcToolStripStatusLabel.Spring = true;
this.calcToolStripStatusLabel.Text = "Ready";
this.calcToolStripStatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// digitsSoFarToolStripStatusLabel
//
this.digitsSoFarToolStripStatusLabel.Name = "digitsSoFarToolStripStatusLabel";
this.digitsSoFarToolStripStatusLabel.Size = new System.Drawing.Size(0, 17);
//
// calcToolStripProgressBar
//
this.calcToolStripProgressBar.Name = "calcToolStripProgressBar";
this.calcToolStripProgressBar.Size = new System.Drawing.Size(100, 16);
this.calcToolStripProgressBar.Step = 1;
this.calcToolStripProgressBar.Visible = false;
//
// backgroundWorker
//
this.backgroundWorker.WorkerReportsProgress = true;
this.backgroundWorker.WorkerSupportsCancellation = true;
this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
this.backgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker_ProgressChanged);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted);
//
// AsyncCalcPiForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(583, 323);
this.Controls.Add(this.resultsTextBox);
this.Controls.Add(this.progressStatusStrip);
this.Controls.Add(this.panel1);
this.MinimumSize = new System.Drawing.Size(249, 180);
this.Name = "AsyncCalcPiForm";
this.Text = "Digits of Pi";
((System.ComponentModel.ISupportInitialize)(this.decimalPlacesNumericUpDown)).EndInit();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.progressStatusStrip.ResumeLayout(false);
this.progressStatusStrip.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
//////////
void ShowProgress(string pi, int totalDigits, int digitsSoFar)
{
// Make sure we're on the UI thread
Debug.Assert(this.InvokeRequired == false);
// Display progress in UI
this.resultsTextBox.Text = pi;
this.calcToolStripProgressBar.Maximum = totalDigits;
this.calcToolStripProgressBar.Value = digitsSoFar;
}
class CalcPiUserState
{
public readonly string Pi;
public readonly int TotalDigits;
public readonly int DigitsSoFar;
public CalcPiUserState(string pi, int totalDigits, int digitsSoFar)
{
this.Pi = pi;
this.TotalDigits = totalDigits;
this.DigitsSoFar = digitsSoFar;
}
}
void CalcPi(int digits)
{
StringBuilder pi = new StringBuilder("3", digits + 2);
// Report initial progress
this.backgroundWorker.ReportProgress(0,
new CalcPiUserState(pi.ToString(), digits, 0));
if (digits > 0)
{
pi.Append(".");
for (int i = 0; i < digits; i += 9)
{
int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
int digitCount = Math.Min(digits - i, 9);
string ds = string.Format("{0:D9}", nineDigits);
pi.Append(ds.Substring(0, digitCount));
// Report continuing progress
this.backgroundWorker.ReportProgress(0,
new CalcPiUserState(pi.ToString(), digits, i + digitCount));
// Check for cancelation
if (this.backgroundWorker.CancellationPending) { return; }
}
}
}
void calcButton_Click(object sender, EventArgs e)
{
// Don't process if cancel request pending
// (Should not be called, since we disabled the button...)
if (this.backgroundWorker.CancellationPending) { return; }
// If worker thread currently executing, cancel it
if (this.backgroundWorker.IsBusy)
{
this.calcButton.Enabled = false;
this.backgroundWorker.CancelAsync();
return;
}
// Set calculating UI
this.calcButton.Text = "Cancel";
this.calcToolStripProgressBar.Visible = true;
this.calcToolStripStatusLabel.Text = "Calculating...";
// Begin calculating pi asynchronously
this.backgroundWorker.RunWorkerAsync(
(int)this.decimalPlacesNumericUpDown.Value);
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//throw new Exception("Ooops!");
// Track start time
DateTime start = DateTime.Now;
CalcPi((int)e.Argument);
// Indicate cancelation
if (this.backgroundWorker.CancellationPending)
{
e.Cancel = true;
}
// Return elapsed time
DateTime end = DateTime.Now;
TimeSpan elapsed = end - start;
e.Result = elapsed;
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Show progress
CalcPiUserState progress = (CalcPiUserState)e.UserState;
ShowProgress(
progress.Pi, progress.TotalDigits, progress.DigitsSoFar);
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Reset progress UI
this.calcButton.Text = "Calculate";
this.calcButton.Enabled = true;
this.calcToolStripStatusLabel.Text = "Ready";
this.calcToolStripProgressBar.Visible = false;
// Was the worker thread cancelled?
if (e.Cancelled)
{
this.resultsTextBox.Text = "Canceled";
return;
}
// Show elapsed time
TimeSpan elapsed = (TimeSpan)e.Result;
MessageBox.Show("Elapsed: " + elapsed.ToString());
}
}
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// NineDigitsOfPiAt.cs
/*
* Computation of the n'th decimal digit of pi with very little memory.
* Written by Fabrice Bellard on January 8, 1997.
* Ported to C# by Chris Sells on May 5, 2002.
*
* We use a slightly modified version of the method described by Simon
* Plouffe in "On the Computation of the n'th decimal digit of various
* transcendental numbers" (November 1996). We have modified the algorithm
* to get a running time of O(n^2) instead of O(n^3log(n)^3).
*
* This program uses mostly integer arithmetic. It may be slow on some
* hardwares where integer multiplications and divisons must be done
* by software.
*/
public class NineDigitsOfPi
{
public static int StartingAt(int n)
{
int av = 0;
int vmax = 0;
int N = (int)((n + 20) * Math.Log(10) / Math.Log(2));
int num = 0;
int den = 0;
int kq = 0;
int kq2 = 0;
int t = 0;
int v = 0;
int s = 0;
double sum = 0.0;
for (int a = 3; a <= (2 * N); a = next_prime(a))
{
vmax = (int)(Math.Log(2 * N) / Math.Log(a));
av = 1;
for (int i = 0; i < vmax; ++i) av = av * a;
s = 0;
num = 1;
den = 1;
v = 0;
kq = 1;
kq2 = 1;
for (int k = 1; k <= N; ++k)
{
t = k;
if (kq >= a)
{
do
{
t = t / a;
--v;
}
while ((t % a) == 0);
kq = 0;
}
++kq;
num = mul_mod(num, t, av);
t = (2 * k - 1);
if (kq2 >= a)
{
if (kq2 == a)
{
do
{
t = t / a;
++v;
}
while ((t % a) == 0);
}
kq2 -= a;
}
den = mul_mod(den, t, av);
kq2 += 2;
if (v > 0)
{
t = inv_mod(den, av);
t = mul_mod(t, num, av);
t = mul_mod(t, k, av);
for (int i = v; i < vmax; ++i) t = mul_mod(t, a, av);
s += t;
if (s >= av) s -= av;
}
}
t = pow_mod(10, n - 1, av);
s = mul_mod(s, t, av);
sum = (sum + (double)s / (double)av) % 1.0;
}
return (int)(sum * 1e9);
}
private static int mul_mod(long a, long b, int m)
{
return (int)((a * b) % m);
}
// return the inverse of x mod y
private static int inv_mod(int x, int y)
{
int q = 0;
int u = x;
int v = y;
int a = 0;
int c = 1;
int t = 0;
do
{
q = v / u;
t = c;
c = a - q * c;
a = t;
t = u;
u = v - q * u;
v = t;
}
while (u != 0);
a = a % y;
if (a < 0) a = y + a;
return a;
}
// return (a^b) mod m
private static int pow_mod(int a, int b, int m)
{
int r = 1;
int aa = a;
while (true)
{
if ((b & 0x01) != 0) r = mul_mod(r, aa, m);
b = b >> 1;
if (b == 0) break;
aa = mul_mod(aa, aa, m);
}
return r;
}
// return true if n is prime
private static bool is_prime(int n)
{
if ((n % 2) == 0) return false;
int r = (int)(Math.Sqrt(n));
for (int i = 3; i <= r; i += 2)
{
if ((n % i) == 0) return false;
}
return true;
}
// return the prime number immediately after n
private static int next_prime(int n)
{
do
{
n++;
}
while (!is_prime(n));
return n;
}
}