Hi,all
Zeta(z)=1/1^z+1/2^z+1/3^z+...+1/n^z+...
n: All natural numbers
z = a+bi, a != 1
Zeta(z) trivial zeros: a = -2,-4,-6,...,-2n
Zeta(z) Nontrivial zeros: a = 1/2, b = ?(40%) [There isn't still strict proof now]
The Zeta(z) Nontrivial zeros are related to the count of prime numbers.
I drawed the Zeta(z) curve. but it is strange. Do you know how to draw the following left image?
I havent a clue, but it looks very similar in some aspects to a cardioid polar pattern (https://www.lewitt-audio.com/blog/cardioid-polar-pattern), at least the outermost curves.. except for the orientation.
(https://i.postimg.cc/fWS6vmwY/polar-pattern-03-transp-0.png)
Probably doesn't help you at all, but it was the first thing that came to mind when I saw your image on the left.
Try this (press R to reset animation). You can also save the generated image
Links below holds the full source (and solution database for VS2022)
https://www.mediafire.com/file/t4bxo1lgvc2bdm8/Zeta10.rar/file (https://www.mediafire.com/file/t4bxo1lgvc2bdm8/Zeta10.rar/file)
https://www.mediafire.com/file/2axp5fyma44y6on/Zeta11.zip/file (https://www.mediafire.com/file/2axp5fyma44y6on/Zeta11.zip/file)
(https://i.postimg.cc/xN3t1HHw/Clipboard-05-09-2025-01.png) (https://postimg.cc/xN3t1HHw)
// ZetaVisualizerFinal.c - Extended coordinate labels to match grid lines
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <commdlg.h>
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
using namespace Gdiplus;
// Global variables for animation
static float animationProgress = 0.0f; // 0.0 to 1.0
static const float ANIMATION_SPEED = 0.005f; // Adjust for faster/slower animation
static const UINT_PTR ANIMATION_TIMER_ID = 1;
static bool isAnimationRunning = false; // Track animation state
// Complex number structure
typedef struct {
double real;
double imag;
} Complex;
// Improved Zeta function with better convergence
Complex Zeta(Complex s, int iterations) {
Complex result = { 0.0, 0.0 };
double running_sum = 0.0;
// Standard Dirichlet series for first N terms
for (int n = 1; n <= iterations; n++) {
double term = 1.0 / pow(n, s.real);
double angle = -s.imag * log(n);
result.real += term * cos(angle);
result.imag += term * sin(angle);
if (n > 10 && fabs(result.real - running_sum) < 1e-10) {
break;
}
running_sum = result.real;
}
// Add a simple Euler-Maclaurin correction for remainder
if (iterations > 10) {
int N = iterations;
double term = 1.0 / pow(N + 1, s.real);
double angle = -s.imag * log(N + 1);
// Approximate remainder: integral term ~ 1/(s-1) * N^(1-s)
double correction_real = term * cos(angle) / (s.real - 1.0);
double correction_imag = term * sin(angle) / (s.real - 1.0);
result.real += correction_real;
result.imag += correction_imag;
}
return result;
}
// Draw the enhanced Zeta function visualization
void DrawEnhancedZeta(HDC hdc, int width, int height, float progress) {
Graphics graphics(hdc);
graphics.SetSmoothingMode(SmoothingModeAntiAlias);
graphics.SetCompositingQuality(CompositingQualityHighQuality);
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias); // Improve text rendering
// Dark gradient background
LinearGradientBrush background(
Point(0, 0),
Point(width, height),
Color(255, 10, 20, 50), // Deep blue
Color(255, 0, 0, 0)); // Black
graphics.FillRectangle(&background, 0, 0, width, height);
// Set up coordinate system
REAL centerX = width / 2.0f;
REAL centerY = height / 2.0f;
REAL scale = (REAL)min(width, height) / 4.0f;
REAL unitScale = scale / 6.0f; // Pixels per unit (zeta.real or zeta.imag)
// Draw subtle grid lines
Pen gridPen(Color(50, 100, 100, 150), 0.3f);
for (int i = -6; i <= 6; i++) {
graphics.DrawLine(&gridPen,
0.0f, centerY + i * scale / 3.0f,
(REAL)width, centerY + i * scale / 3.0f);
graphics.DrawLine(&gridPen,
centerX + i * scale / 3.0f, 0.0f,
centerX + i * scale / 3.0f, (REAL)height);
}
// Draw glowing axes
LinearGradientBrush axisBrush(
Point(0, (int)centerY),
Point(width, (int)centerY),
Color(150, 200, 200, 255),
Color(50, 100, 100, 100));
Pen axisPen(&axisBrush, 1.8f);
graphics.DrawLine(&axisPen, 0.0f, centerY, (REAL)width, centerY);
graphics.DrawLine(&axisPen, centerX, 0.0f, centerX, (REAL)height);
// Draw critical line
LinearGradientBrush criticalBrush(
Point((int)centerX, 0),
Point((int)centerX, height),
Color(150, 255, 150, 50),
Color(50, 100, 255, 50));
Pen criticalPen(&criticalBrush, 2.0f);
REAL criticalX = centerX + scale * 0.5f;
graphics.DrawLine(&criticalPen, criticalX, 0.0f, criticalX, (REAL)height);
// Draw Zeta function curve with animation
const int iterations = 200;
const double t_start = 0.0;
const double t_end = 30.0;
const double t_step = 0.02;
double t_max = t_start + progress * (t_end - t_start); // Animate up to t_max
GraphicsPath path;
PointF prevPoint;
bool firstPoint = true;
for (double t = t_start; t <= t_max; t += t_step) {
Complex s = { 0.5, t };
Complex zeta = Zeta(s, iterations);
REAL x = centerX + (REAL)zeta.imag * scale / 6.0f;
REAL y = centerY - (REAL)zeta.real * scale / 6.0f;
if (!firstPoint) {
path.AddLine(prevPoint, PointF(x, y));
}
else {
path.StartFigure();
firstPoint = false;
}
prevPoint = PointF(x, y);
}
// Neon gradient for Zeta curve
Color colors[] = {
Color(200, 255, 50, 50),
Color(200, 255, 165, 0),
Color(200, 255, 0, 255),
Color(200, 50, 50, 255)
};
REAL positions[] = { 0.0f, 0.33f, 0.67f, 1.0f };
LinearGradientBrush pathBrush(
Point(0, (int)(centerY - scale)),
Point(0, (int)(centerY + scale)),
Color(255, 255, 255, 255),
Color(255, 255, 255, 255));
pathBrush.SetInterpolationColors(colors, positions, 4);
// Draw glow and main curve
Pen glowPen(&pathBrush, 5.0f);
glowPen.SetColor(Color(100, 255, 255, 255));
graphics.DrawPath(&glowPen, &path);
Pen zetaPen(&pathBrush, 2.5f);
graphics.DrawPath(&zetaPen, &path);
// Add annotations with adjusted positions
Font font(L"Cambria Math", 16, FontStyleBold);
Font smallFont(L"Cambria Math", 12, FontStyleRegular); // Smaller font for coordinate labels
SolidBrush shadowBrush(Color(80, 0, 0, 0));
SolidBrush textBrush(Color(255, 230, 230, 230));
StringFormat format;
format.SetAlignment(StringAlignmentCenter);
// Title: "ζ(s) where s = ½ + ti" (top center)
Font bigFont(L"Cambria Math", 20, FontStyleBold);
graphics.DrawString(L"ζ(s) where s = ½ + ti", -1, &bigFont,
PointF(centerX + 4.0f, 24.0f), &format, &shadowBrush);
graphics.DrawString(L"ζ(s) where s = ½ + ti", -1, &bigFont,
PointF(centerX, 20.0f), &format, &textBrush);
// Critical line label: "Re(s) = ½" (right side, near critical line)
graphics.DrawString(L"Re(s) = ½", -1, &font,
PointF(criticalX + 4.0f, 60.0f), &format, &shadowBrush);
graphics.DrawString(L"Re(s) = ½", -1, &font,
PointF(criticalX, 56.0f), &format, &textBrush);
// Axis label: "Im(ζ(s))" (right side)
REAL imLabelX = (REAL)width - 100.0f;
graphics.DrawString(L"Im(ζ(s))", -1, &font,
PointF(imLabelX + 4.0f, centerY - 24.0f), &shadowBrush);
graphics.DrawString(L"Im(ζ(s))", -1, &font,
PointF(imLabelX, centerY - 20.0f), &textBrush);
// Axis label: "Re(ζ(s))" (bottom center)
graphics.DrawString(L"Re(ζ(s))", -1, &font,
PointF(centerX + 14.0f, (REAL)height - 44.0f), &shadowBrush);
graphics.DrawString(L"Re(ζ(s))", -1, &font,
PointF(centerX + 10.0f, (REAL)height - 40.0f), &textBrush);
// Add coordinate labels for horizontal axis (Im(ζ(s))): -12 to 12, step 2
for (int i = -6; i <= 6; i++) {
int value = i * 2; // Grid lines are at intervals of 2 units
REAL xPos = centerX + value * unitScale;
WCHAR label[16];
swprintf(label, 16, L"%d", value);
graphics.DrawString(label, -1, &smallFont,
PointF(xPos + 2.0f, centerY + 16.0f), &format, &shadowBrush);
graphics.DrawString(label, -1, &smallFont,
PointF(xPos, centerY + 12.0f), &format, &textBrush);
}
// Add coordinate labels for vertical axis (Re(ζ(s))): -12 to 12, step 2
StringFormat nearFormat;
nearFormat.SetAlignment(StringAlignmentNear); // Left-align for vertical labels
for (int i = -6; i <= 6; i++) {
int value = i * 2; // Grid lines are at intervals of 2 units
REAL yPos = centerY - value * unitScale; // Negative because screen y increases downward
WCHAR label[16];
if (value == 0) {
swprintf(label, 16, L"0");
}
else {
swprintf(label, 16, L"%di", value);
}
graphics.DrawString(label, -1, &smallFont,
PointF(centerX - 30.0f + 2.0f, yPos + 2.0f), &nearFormat, &shadowBrush);
graphics.DrawString(label, -1, &smallFont,
PointF(centerX - 30.0f, yPos), &nearFormat, &textBrush);
}
// Add animated non-trivial zero markers
double zeros[] = { 14.1347, 21.0220, 25.0109, 30.4249 }; // Known zeros
int numZeros = sizeof(zeros) / sizeof(zeros[0]);
for (int i = 0; i < numZeros; i++) {
if (zeros[i] <= t_max) { // Only draw zeros up to current progress
Complex s = { 0.5, zeros[i] };
Complex zeta = Zeta(s, iterations);
REAL x = centerX + (REAL)zeta.imag * scale / 6.0f;
REAL y = centerY - (REAL)zeta.real * scale / 6.0f;
LinearGradientBrush zeroBrush(
Point((int)(x - 10.0f), (int)(y - 10.0f)),
Point((int)(x + 10.0f), (int)(y + 10.0f)),
Color(200, 255, 100, 100),
Color(0, 255, 255, 255));
graphics.FillEllipse(&zeroBrush, x - 7.0f, y - 7.0f, 14.0f, 14.0f);
Pen zeroPen(Color(150, 255, 255, 255), 1.0f);
graphics.DrawEllipse(&zeroPen, x - 7.0f, y - 7.0f, 14.0f, 14.0f);
}
}
}
// Helper function to get PNG encoder CLSID
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
UINT num = 0, size = 0;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0) return -1;
ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
// Save visualization as PNG
void SaveAsPng(HWND hWnd, int width, int height, const WCHAR* filename) {
// Create a bitmap
Bitmap bitmap(width, height, PixelFormat32bppARGB);
Graphics bitmapGraphics(&bitmap);
bitmapGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
bitmapGraphics.SetCompositingQuality(CompositingQualityHighQuality);
bitmapGraphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
// Draw the full visualization (progress = 1.0)
HDC hdc = bitmapGraphics.GetHDC();
DrawEnhancedZeta(hdc, width, height, 1.0f);
bitmapGraphics.ReleaseHDC(hdc);
// Save as PNG
CLSID clsid;
if (GetEncoderClsid(L"image/png", &clsid) != -1) {
bitmap.Save(filename, &clsid, NULL);
char msg[512];
snprintf(msg, sizeof(msg), "Visualization saved as %S", filename);
MessageBoxA(hWnd, msg, "Success", MB_OK | MB_ICONINFORMATION);
}
else {
MessageBoxA(hWnd, "Failed to save PNG: PNG encoder not found", "Error", MB_OK | MB_ICONERROR);
}
}
// Open file dialog for saving PNG
void ShowSaveDialog(HWND hWnd, int width, int height) {
WCHAR filename[MAX_PATH] = L"ZetaVisualization.png";
OPENFILENAMEW ofn = { 0 };
ofn.lStructSize = sizeof(OPENFILENAMEW);
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = L"PNG Files (*.png)\0*.png\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrDefExt = L"png";
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
if (GetSaveFileNameW(&ofn)) {
SaveAsPng(hWnd, width, height, filename);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static int width, height;
switch (message) {
case WM_CREATE:
// Animation starts paused; no timer set initially
break;
case WM_TIMER:
if (wParam == ANIMATION_TIMER_ID) {
animationProgress += ANIMATION_SPEED;
if (animationProgress > 1.0f) {
animationProgress = 1.0f; // Stop at full progress
KillTimer(hWnd, ANIMATION_TIMER_ID);
isAnimationRunning = false;
}
InvalidateRect(hWnd, NULL, FALSE); // FALSE to avoid background erase
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
width = rect.right;
height = rect.bottom;
// Create double buffer
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP memBitmap = CreateCompatibleBitmap(hdc, width, height);
HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
// Fill background to avoid artifacts
HBRUSH blackBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
FillRect(memDC, &rect, blackBrush);
// Draw to memory DC
DrawEnhancedZeta(memDC, width, height, animationProgress);
// Copy to screen
BitBlt(hdc, 0, 0, width, height, memDC, 0, 0, SRCCOPY);
// Clean up
SelectObject(memDC, oldBitmap);
DeleteObject(memBitmap);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
break;
}
case WM_SIZE:
width = LOWORD(lParam);
height = HIWORD(lParam);
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case 1001: // Save As
ShowSaveDialog(hWnd, width, height);
break;
case 1002: // Start Animation
if (!isAnimationRunning) {
SetTimer(hWnd, ANIMATION_TIMER_ID, 16, NULL); // ~60 FPS
isAnimationRunning = true;
}
break;
case 1003: // Pause Animation
if (isAnimationRunning) {
KillTimer(hWnd, ANIMATION_TIMER_ID);
isAnimationRunning = false;
}
break;
}
break;
case WM_KEYDOWN:
if (wParam == 'R') { // Reset animation
animationProgress = 0.0f;
if (!isAnimationRunning) {
SetTimer(hWnd, ANIMATION_TIMER_ID, 16, NULL);
isAnimationRunning = true;
}
InvalidateRect(hWnd, NULL, FALSE);
}
break;
case WM_DESTROY:
if (isAnimationRunning) {
KillTimer(hWnd, ANIMATION_TIMER_ID);
}
PostQuitMessage(0);
break;
default:
return DefWindowProcA(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Create menu
HMENU hMenu = CreateMenu();
HMENU hFileMenu = CreatePopupMenu();
AppendMenuA(hFileMenu, MF_STRING, 1001, "Save As...");
AppendMenuA(hFileMenu, MF_STRING, 1002, "Start Animation");
AppendMenuA(hFileMenu, MF_STRING, 1003, "Pause Animation");
AppendMenuA(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, "File");
WNDCLASSA wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = "ZetaVisualizer";
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
RegisterClassA(&wc);
HWND hWnd = CreateWindowA("ZetaVisualizer", "Riemann Zeta Function Visualizer",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
1200, 900, NULL, hMenu, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
DestroyMenu(hMenu);
GdiplusShutdown(gdiplusToken);
return (int)msg.wParam;
}
Attached the binary file. And on the links, the full source
hi guga
I did a zeta approximation in FreeBasic years ago using the following
' f(k)=(k+nc)^-n
' inf nc inf inf
' ==== ==== / ==== B
' \ 1 \ 1 [ \ (2*k) (2*k-1)
' > ----- = > ----- + I f(k) dk + B * f(0) - > ------ f (0)
' / n / n ] 1 / (2*k)!
' ==== k ==== k / ====
' k = 1 k = 1 0 k = 1
I posted the code here https://www.freebasic.net/forum/viewtopic.php?p=193860#p193860 (https://www.freebasic.net/forum/viewtopic.php?p=193860#p193860)
Quote from: jack on May 10, 2025, 01:27:48 AMhi guga
I did a zeta approximation in FreeBasic years ago using the following
' f(k)=(k+nc)^-n
' inf nc inf inf
' ==== ==== / ==== B
' \ 1 \ 1 [ \ (2*k) (2*k-1)
' > ----- = > ----- + I f(k) dk + B * f(0) - > ------ f (0)
' / n / n ] 1 / (2*k)!
' ==== k ==== k / ====
' k = 1 k = 1 0 k = 1
I can post the code if anyone is interested
Tks, Jack. It would be good to see the code. Maybe it can later be ported to masm or even plain C if needed
I edited my post with a link to the source
Quote from: jack on May 10, 2025, 01:36:15 AMI edited my post with a link to the source
Tks, try this. Grok ported your version.
https://www.mediafire.com/file/bpmn4b5pj74ka2m/Zeta12.rar/file (https://www.mediafire.com/file/bpmn4b5pj74ka2m/Zeta12.rar/file)
(https://i.postimg.cc/hfYzXH5P/Clipboard-05-09-2025-01.png) (https://postimg.cc/hfYzXH5P)
hey guga
Grok explained what the code does better than my comments in my code :biggrin:
Quote from: jack on May 10, 2025, 07:39:56 AMhey guga
Grok explained what the code does better than my comments in my code :biggrin:
Indeed :biggrin: Grok is a wonderful tool. I started 1st using deepseek to create some basic app that worked similar to the left image that six_L showed. But, the result was inaccurate. Nevertheless, i took the code generated on deepseek and used it through Grok (the x version and not the one from the site) to it fix the code and make it more similar to the image, enhancing graphics and focusing in using gdi+. It fixed and i started to play with it, adding animation, a menu etc. Then when you uploaded your freebasic version, since i already had the skeleton, i mainly asked Grok to adapt the code he just did in C to yours in freebasic.
Grok does a very good work in Plain C language, but still lack the same skill in what concerns assembly (no matter if for masm, rosasm, fasm or nasm). For assembly programming you must not trust 100% on the generated code, but use it mainly as a guidance to improve yours.
In any case, the results i´ve seen so far are good enough to save some time in development. Most likely, until the end of the year i believe Grok (and others) should be more advanced in what is related to assembly language to save us even more time for coding.
One thing interesting, i have no idea what is this zeta all about. I only realized what was that, after pasting the comments six_L did on deepseek and ask him to do an app based on this. The rest, was fine tunned with Grok.
I started on deepseek, simply inserting this:
QuotePls create me a function in plain C using Gdi+ on a way i can generate the graphic on this link (image on left)https://masm32.com/board/index.php?action=dlattach;attach=18375;image. The goal is: Hi,all
Zeta(z)=1/1^z+1/2^z+1/3^z+...+1/n^z+...
n: All natural numbers
z = a+bi, a != 1
Zeta(z) trivial zeros: a = -2,-4,-6,...,-2n
Zeta(z) Nontrivial zeros: a = 1/2, b = ?(40%) [There isn't still strict proof now]
The Zeta(z) Nontrivial zeros are related to the count of prime numbers.
I drawed the Zeta(z) curve. but it is strange. Do you know how to draw the following left image?
And that was all it was needed to it create a zeta visualizer in C. Then, as i said, i used the generated code in x for fine tune and fix it.
Hi,zedd/guga/jack
Thanks your help.
Quite useful information.
I am very interested in your Zeta algorithms. I need to learn hardly.
Zeta(z)=1/1^z+1/2^z+1/3^z+...+1/n^z+...
=1/(1-1/2^z) * 1/(1-1/3^z) * 1/(1-1/5^z) * 1/(1-1/7^z) * 1/(1-1/11^z) * ... * 1/(1-1/p^z)
n: All natural numbers
z = a+bi, a != 1
p = All prime numbers
ln(Zeta(z)) = -[ln(1-1/2^z)+ln(1-1/3^z)+ln(1-1/5^z)+ln(1-1/7^z)+...+ln(1-1/p^z)]
= x
Zeta(z) = e^x
150 years ago, no computer, the great mathematicians used their pens to calculate the Zeta(z) Nontrivial zeros. That's a very difficult job. Admiring!
The problem with the previous images was due to the step value being too large.
now it works fine.
Hi Six_L you´re welcome.
Now, try this. (Added zoom with mouse wheel and pg up/down), also it starts (resets) with a space and not only the R key
// ZetaVisualizerFinal.c - Added Page Up/Page Down zoom, smoothed curve, fixed starting line
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <commdlg.h>
#include <vector>
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
using namespace Gdiplus;
// Global variables for animation and zoom
static float animationProgress = 0.0f; // 0.0 to 1.0
static const float ANIMATION_SPEED = 0.005f; // Adjust for faster/slower animation
static const UINT_PTR ANIMATION_TIMER_ID = 1;
static bool isAnimationRunning = false; // Track animation state
static float zoomFactor = 1.0f; // Zoom factor (1.0 = default, >1 zoom in, <1 zoom out)
// Complex number structure
typedef struct {
double real;
double imag;
} Complex;
// Complex arithmetic functions
Complex ComplexAdd(Complex a, Complex b) {
Complex result;
result.real = a.real + b.real;
result.imag = a.imag + b.imag;
return result;
}
Complex ComplexMul(Complex a, Complex b) {
Complex result;
result.real = a.real * b.real - a.imag * b.imag;
result.imag = a.real * b.imag + a.imag * b.real;
return result;
}
Complex ComplexSub(Complex a, Complex b) {
Complex result;
result.real = a.real - b.real;
result.imag = a.imag - b.imag;
return result;
}
Complex ComplexPowReal(Complex base, double exponent) {
double r = sqrt(base.real * base.real + base.imag * base.imag);
double theta = atan2(base.imag, base.real);
double ln_r = log(r);
double exponent_ln_r = exponent * ln_r;
double exponent_theta = exponent * theta;
Complex result;
result.real = exp(exponent_ln_r) * cos(exponent_theta);
result.imag = exp(exponent_ln_r) * sin(exponent_theta);
return result;
}
Complex ComplexPow(Complex base, Complex exponent) {
double r = sqrt(base.real * base.real + base.imag * base.imag);
double theta = atan2(base.imag, base.real);
Complex ln_base;
ln_base.real = log(r);
ln_base.imag = theta;
Complex exponent_ln_base = ComplexMul(exponent, ln_base);
double magnitude = exp(exponent_ln_base.real);
Complex result;
result.real = magnitude * cos(exponent_ln_base.imag);
result.imag = magnitude * sin(exponent_ln_base.imag);
return result;
}
Complex ComplexDiv(Complex a, Complex b) {
double denom = b.real * b.real + b.imag * b.imag;
Complex result;
result.real = (a.real * b.real + a.imag * b.imag) / denom;
result.imag = (a.imag * b.real - a.real * b.imag) / denom;
return result;
}
Complex ComplexScale(Complex a, double scalar) {
Complex result;
result.real = a.real * scalar;
result.imag = a.imag * scalar;
return result;
}
Complex ComplexNeg(Complex a) {
Complex result;
result.real = -a.real;
result.imag = -a.imag;
return result;
}
// Complex logarithm: ln(z) = ln(|z|) + i * arg(z)
Complex ComplexLn(Complex z) {
Complex result;
result.real = log(sqrt(z.real * z.real + z.imag * z.imag));
result.imag = atan2(z.imag, z.real);
return result;
}
// Generate prime numbers using Sieve of Eratosthenes
std::vector<int> GeneratePrimes(int limit) {
std::vector<bool> isPrime(limit + 1, true);
isPrime[0] = isPrime[1] = false;
for (int i = 2; i * i <= limit; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= limit; j += i) {
isPrime[j] = false;
}
}
}
std::vector<int> primes;
for (int i = 2; i <= limit; i++) {
if (isPrime[i]) {
primes.push_back(i);
}
}
return primes;
}
// Zeta function using Euler product: ζ(z) = exp(-∑_p ln(1 - p^(-z)))
Complex Zeta(Complex z, int iterations) {
static std::vector<int> primes;
if (primes.empty()) {
primes = GeneratePrimes(1000);
}
Complex sum = { 0.0, 0.0 };
Complex one = { 1.0, 0.0 };
Complex p_complex, p_neg_z, term;
int count = 0;
for (int p : primes) {
if (count >= iterations) break;
p_complex.real = (double)p;
p_complex.imag = 0.0;
Complex neg_z = ComplexNeg(z);
p_neg_z = ComplexPow(p_complex, neg_z);
term = ComplexSub(one, p_neg_z);
Complex ln_term = ComplexLn(term);
sum = ComplexAdd(sum, ComplexNeg(ln_term));
count++;
}
double magnitude = exp(sum.real);
Complex result;
result.real = magnitude * cos(sum.imag);
result.imag = magnitude * sin(sum.imag);
return result;
}
// Draw the enhanced Zeta function visualization
void DrawEnhancedZeta(HDC hdc, int width, int height, float progress) {
Graphics graphics(hdc);
graphics.SetSmoothingMode(SmoothingModeAntiAlias);
graphics.SetCompositingQuality(CompositingQualityHighQuality);
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
// Dark gradient background
LinearGradientBrush background(
Point(0, 0),
Point(width, height),
Color(255, 10, 20, 50),
Color(255, 0, 0, 0));
graphics.FillRectangle(&background, 0, 0, width, height);
// Set up coordinate system
REAL centerX = width / 2.0f;
REAL centerY = height / 2.0f;
REAL scale = (REAL)min(width, height) / 4.0f;
REAL unitScale = (scale / 12.0f) * zoomFactor; // Adjusted with zoom factor
// Draw subtle grid lines (step of 4 units)
Pen gridPen(Color(50, 100, 100, 150), 0.3f);
// Horizontal axis: Im(ζ(s)), up to ±32
for (int i = -8; i <= 8; i++) { // ±32, step 4
REAL gridPos = i * 4 * unitScale;
graphics.DrawLine(&gridPen,
0.0f, centerY + gridPos,
(REAL)width, centerY + gridPos);
}
// Vertical axis: Re(ζ(s)), up to ±24
for (int i = -6; i <= 6; i++) { // ±24, step 4
REAL gridPos = i * 4 * unitScale;
graphics.DrawLine(&gridPen,
centerX + gridPos, 0.0f,
centerX + gridPos, (REAL)height);
}
// Draw glowing axes
LinearGradientBrush axisBrush(
Point(0, (int)centerY),
Point(width, (int)centerY),
Color(150, 200, 200, 255),
Color(50, 100, 100, 100));
Pen axisPen(&axisBrush, 1.8f);
graphics.DrawLine(&axisPen, 0.0f, centerY, (REAL)width, centerY);
graphics.DrawLine(&axisPen, centerX, 0.0f, centerX, (REAL)height);
// Draw critical line
LinearGradientBrush criticalBrush(
Point((int)centerX, 0),
Point((int)centerX, height),
Color(150, 255, 150, 50),
Color(50, 100, 255, 50));
Pen criticalPen(&criticalBrush, 2.0f);
REAL criticalX = centerX + scale * 0.5f;
graphics.DrawLine(&criticalPen, criticalX, 0.0f, criticalX, (REAL)height);
// Compute Zeta function points and store them
const int iterations = 200;
const double t_start = 0.0;
const double t_end = 30.0;
const double t_step = 0.02;
double t_max = t_start + progress * (t_end - t_start);
// Collect points for the curve
std::vector<PointF> points;
for (double t = t_start; t <= t_max; t += t_step) {
Complex s = { 0.5, t };
Complex zeta = Zeta(s, iterations);
REAL x = centerX + (REAL)zeta.imag * unitScale;
REAL y = centerY - (REAL)zeta.real * unitScale;
points.push_back(PointF(x, y));
}
// Draw the Zeta curve as a smooth spline
GraphicsPath path;
if (!points.empty()) {
path.AddCurve(points.data(), (INT)points.size(), 0.5f); // Tension = 0.5 for smoothness
}
// Neon gradient for Zeta curve
Color colors[] = {
Color(200, 255, 50, 50),
Color(200, 255, 165, 0),
Color(200, 255, 0, 255),
Color(200, 50, 50, 255)
};
REAL positions[] = { 0.0f, 0.33f, 0.67f, 1.0f };
LinearGradientBrush pathBrush(
Point(0, (int)(centerY - scale)),
Point(0, (int)(centerY + scale)),
Color(255, 255, 255, 255),
Color(255, 255, 255, 255));
pathBrush.SetInterpolationColors(colors, positions, 4);
// Draw glow and main curve
Pen glowPen(&pathBrush, 5.0f);
glowPen.SetColor(Color(100, 255, 255, 255));
graphics.DrawPath(&glowPen, &path);
Pen zetaPen(&pathBrush, 2.5f);
graphics.DrawPath(&zetaPen, &path);
// Add annotations with adjusted positions
Font font(L"Cambria Math", 16, FontStyleBold);
Font smallFont(L"Cambria Math", 12, FontStyleRegular);
SolidBrush shadowBrush(Color(80, 0, 0, 0));
SolidBrush textBrush(Color(255, 230, 230, 230));
StringFormat format;
format.SetAlignment(StringAlignmentCenter);
// Title: "ζ(s) where s = ½ + ti"
Font bigFont(L"Cambria Math", 20, FontStyleBold);
graphics.DrawString(L"ζ(s) where s = ½ + ti", -1, &bigFont,
PointF(centerX + 4.0f, 24.0f), &format, &shadowBrush);
graphics.DrawString(L"ζ(s) where s = ½ + ti", -1, &bigFont,
PointF(centerX, 20.0f), &format, &textBrush);
// Critical line label: "Re(s) = ½"
graphics.DrawString(L"Re(s) = ½", -1, &font,
PointF(criticalX + 4.0f, 60.0f), &format, &shadowBrush);
graphics.DrawString(L"Re(s) = ½", -1, &font,
PointF(criticalX, 56.0f), &format, &textBrush);
// Axis label: "Im(ζ(s))"
REAL imLabelX = (REAL)width - 100.0f;
graphics.DrawString(L"Im(ζ(s))", -1, &font,
PointF(imLabelX + 4.0f, centerY - 24.0f), &shadowBrush);
graphics.DrawString(L"Im(ζ(s))", -1, &font,
PointF(imLabelX, centerY - 20.0f), &textBrush);
// Axis label: "Re(ζ(s))"
graphics.DrawString(L"Re(ζ(s))", -1, &font,
PointF(centerX + 14.0f, (REAL)height - 44.0f), &shadowBrush);
graphics.DrawString(L"Re(ζ(s))", -1, &font,
PointF(centerX + 10.0f, (REAL)height - 40.0f), &textBrush);
// Coordinate labels for horizontal axis (Im(ζ(s))): -32 to 32, step 4
for (int i = -8; i <= 8; i++) {
int value = i * 4;
REAL xPos = centerX + value * unitScale;
WCHAR label[16];
swprintf(label, 16, L"%d", value);
graphics.DrawString(label, -1, &smallFont,
PointF(xPos + 2.0f, centerY + 16.0f), &format, &shadowBrush);
graphics.DrawString(label, -1, &smallFont,
PointF(xPos, centerY + 12.0f), &format, &textBrush);
}
// Coordinate labels for vertical axis (Re(ζ(s))): -24 to 24, step 4
StringFormat nearFormat;
nearFormat.SetAlignment(StringAlignmentNear);
for (int i = -6; i <= 6; i++) {
int value = i * 4;
REAL yPos = centerY - value * unitScale;
WCHAR label[16];
if (value == 0) {
swprintf(label, 16, L"0");
}
else {
swprintf(label, 16, L"%di", value);
}
graphics.DrawString(label, -1, &smallFont,
PointF(centerX - 30.0f + 2.0f, yPos + 2.0f), &nearFormat, &shadowBrush);
graphics.DrawString(label, -1, &smallFont,
PointF(centerX - 30.0f, yPos), &nearFormat, &textBrush);
}
// Add animated non-trivial zero markers
double zeros[] = { 14.1347, 21.0220, 25.0109, 30.4249 };
int numZeros = sizeof(zeros) / sizeof(zeros[0]);
for (int i = 0; i < numZeros; i++) {
if (zeros[i] <= t_max) {
Complex s = { 0.5, zeros[i] };
Complex zeta = Zeta(s, iterations);
REAL x = centerX + (REAL)zeta.imag * unitScale;
REAL y = centerY - (REAL)zeta.real * unitScale;
LinearGradientBrush zeroBrush(
Point((int)(x - 10.0f), (int)(y - 10.0f)),
Point((int)(x + 10.0f), (int)(y + 10.0f)),
Color(200, 255, 100, 100),
Color(0, 255, 255, 255));
graphics.FillEllipse(&zeroBrush, x - 7.0f, y - 7.0f, 14.0f, 14.0f);
Pen zeroPen(Color(150, 255, 255, 255), 1.0f);
graphics.DrawEllipse(&zeroPen, x - 7.0f, y - 7.0f, 14.0f, 14.0f);
}
}
}
// Helper function to get PNG encoder CLSID
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
UINT num = 0, size = 0;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0) return -1;
ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
// Save visualization as PNG
void SaveAsPng(HWND hWnd, int width, int height, const WCHAR* filename) {
Bitmap bitmap(width, height, PixelFormat32bppARGB);
Graphics bitmapGraphics(&bitmap);
bitmapGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
bitmapGraphics.SetCompositingQuality(CompositingQualityHighQuality);
bitmapGraphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
HDC hdc = bitmapGraphics.GetHDC();
DrawEnhancedZeta(hdc, width, height, 1.0f);
bitmapGraphics.ReleaseHDC(hdc);
CLSID clsid;
if (GetEncoderClsid(L"image/png", &clsid) != -1) {
bitmap.Save(filename, &clsid, NULL);
char msg[512];
snprintf(msg, sizeof(msg), "Visualization saved as %S", filename);
MessageBoxA(hWnd, msg, "Success", MB_OK | MB_ICONINFORMATION);
}
else {
MessageBoxA(hWnd, "Failed to save PNG: PNG encoder not found", "Error", MB_OK | MB_ICONERROR);
}
}
// Open file dialog for saving PNG
void ShowSaveDialog(HWND hWnd, int width, int height) {
WCHAR filename[MAX_PATH] = L"ZetaVisualization.png";
OPENFILENAMEW ofn = { 0 };
ofn.lStructSize = sizeof(OPENFILENAMEW);
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = L"PNG Files (*.png)\0*.png\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrDefExt = L"png";
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
if (GetSaveFileNameW(&ofn)) {
SaveAsPng(hWnd, width, height, filename);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static int width, height;
switch (message) {
case WM_CREATE:
break;
case WM_TIMER:
if (wParam == ANIMATION_TIMER_ID) {
animationProgress += ANIMATION_SPEED;
if (animationProgress > 1.0f) {
animationProgress = 1.0f;
KillTimer(hWnd, ANIMATION_TIMER_ID);
isAnimationRunning = false;
}
InvalidateRect(hWnd, NULL, FALSE);
}
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
width = rect.right;
height = rect.bottom;
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP memBitmap = CreateCompatibleBitmap(hdc, width, height);
HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
HBRUSH blackBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
FillRect(memDC, &rect, blackBrush);
DrawEnhancedZeta(memDC, width, height, animationProgress);
BitBlt(hdc, 0, 0, width, height, memDC, 0, 0, SRCCOPY);
SelectObject(memDC, oldBitmap);
DeleteObject(memBitmap);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
break;
}
case WM_SIZE:
width = LOWORD(lParam);
height = HIWORD(lParam);
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_MOUSEWHEEL: {
short delta = GET_WHEEL_DELTA_WPARAM(wParam);
float zoomStep = 1.1f;
if (delta > 0) {
// Scroll up: Zoom in
zoomFactor *= zoomStep;
if (zoomFactor > 3.0f) zoomFactor = 3.0f;
}
else if (delta < 0) {
// Scroll down: Zoom out
zoomFactor /= zoomStep;
if (zoomFactor < 0.5f) zoomFactor = 0.5f;
}
InvalidateRect(hWnd, NULL, FALSE);
break;
}
case WM_COMMAND:
switch (LOWORD(wParam)) {
case 1001:
ShowSaveDialog(hWnd, width, height);
break;
case 1002:
if (!isAnimationRunning) {
SetTimer(hWnd, ANIMATION_TIMER_ID, 16, NULL);
isAnimationRunning = true;
}
break;
case 1003:
if (isAnimationRunning) {
KillTimer(hWnd, ANIMATION_TIMER_ID);
isAnimationRunning = false;
}
break;
}
break;
case WM_KEYDOWN:
switch (wParam) {
case 'R': // Reset animation
case VK_SPACE: // Also reset with Space key
animationProgress = 0.0f;
if (!isAnimationRunning) {
SetTimer(hWnd, ANIMATION_TIMER_ID, 16, NULL);
isAnimationRunning = true;
}
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_PRIOR: // Page Up: Zoom in
zoomFactor *= 1.1f;
if (zoomFactor > 3.0f) zoomFactor = 3.0f;
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_NEXT: // Page Down: Zoom out
zoomFactor /= 1.1f;
if (zoomFactor < 0.5f) zoomFactor = 0.5f;
InvalidateRect(hWnd, NULL, FALSE);
break;
}
break;
case WM_DESTROY:
if (isAnimationRunning) {
KillTimer(hWnd, ANIMATION_TIMER_ID);
}
PostQuitMessage(0);
break;
default:
return DefWindowProcA(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HMENU hMenu = CreateMenu();
HMENU hFileMenu = CreatePopupMenu();
AppendMenuA(hFileMenu, MF_STRING, 1001, "Save As...");
AppendMenuA(hFileMenu, MF_STRING, 1002, "Start Animation");
AppendMenuA(hFileMenu, MF_STRING, 1003, "Pause Animation");
AppendMenuA(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, "File");
WNDCLASSA wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = "ZetaVisualizer";
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
RegisterClassA(&wc);
HWND hWnd = CreateWindowA("ZetaVisualizer", "Riemann Zeta Function Visualizer",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
1200, 900, NULL, hMenu, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
DestroyMenu(hMenu);
GdiplusShutdown(gdiplusToken);
return (int)msg.wParam;
}
Full project on this link: https://www.mediafire.com/file/kbwfvysh83cc33x/Zeta14.rar/file (https://www.mediafire.com/file/kbwfvysh83cc33x/Zeta14.rar/file)
(https://i.postimg.cc/V085wLYK/Zeta-Visualization.png) (https://postimg.cc/V085wLYK)
Hi,guga
good works!
:thumbsup:
I want to compare your image. Could you post an image of the following parameters?
step = 0.012
a = 1/2,b = [-1025.4712,-1015.1512]
Scale = 50:1
iterations = 860
Thank you in advance.
Regards
six_L
A test project for pure C with Pelles C.
Just for testing those functions, no graphics.