The MASM Forum

64 bit assembler => UASM Assembler Development => Topic started by: six_L on May 09, 2025, 11:06:09 PM

Title: Riemann Zeta Function
Post by: six_L on May 09, 2025, 11:06:09 PM
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?
Title: Re: Riemann Zeta Function
Post by: zedd on May 10, 2025, 12:09:20 AM
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.


Title: Re: Riemann Zeta Function
Post by: guga on May 10, 2025, 12:58:18 AM
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
Title: Re: Riemann Zeta Function
Post by: jack on May 10, 2025, 01:27:48 AM
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)
Title: Re: Riemann Zeta Function
Post by: guga on May 10, 2025, 01:31:50 AM
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
Title: Re: Riemann Zeta Function
Post by: jack on May 10, 2025, 01:36:15 AM
I edited my post with a link to the source
Title: Re: Riemann Zeta Function
Post by: guga on May 10, 2025, 01:43:20 AM
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)
Title: Re: Riemann Zeta Function
Post by: jack on May 10, 2025, 07:39:56 AM
hey guga
Grok explained what the code does better than my comments in my code  :biggrin:
Title: Re: Riemann Zeta Function
Post by: guga on May 10, 2025, 08:49:36 AM
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.
Title: Re: Riemann Zeta Function
Post by: six_L on May 10, 2025, 11:22:29 PM
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.
Title: Re: Riemann Zeta Function
Post by: guga on May 11, 2025, 12:51:03 AM
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)
Title: Re: Riemann Zeta Function
Post by: six_L on May 11, 2025, 03:38:00 PM
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
Title: Re: Riemann Zeta Function
Post by: TimoVJL on May 12, 2025, 12:23:52 AM
A test project for pure C with Pelles C.
Just for testing those functions, no graphics.