POV-Ray : Newsgroups : povray.pov4.discussion.general : Complex Numbers : Complex Numbers Server Time
30 Oct 2025 23:00:03 EDT (-0400)
  Complex Numbers  
From: Bald Eagle
Date: 29 Oct 2025 14:40:00
Message: <web.69025e9441cbf6a37459de3125979125@news.povray.org>
"Given the code base at https://github.com/POV-Ray/povray, what would be
required to fully implement robust  code to handle complex numbers - complete
with addition, subtraction, division, multiplication, and exponentiation - using
both complex and real numbers in any combination?"

Copilot said:


Reasoned for 72s



string, etc., and user‑defined functions (the ones you can put into
isosurface, pigments, etc.) are limited to float math with a fixed set of float


new value type end‑to‑end: tokenizer → parser/AST →
expression evaluator → function VM, plus SDL syntax, promotion rules,
built‑ins, tests, and docs. Below is a concrete implementation plan tied
to the current repo layout.
povray
+2

0) Where complex would fit into the current architecture
Repo & parser locations. The core language is implemented in source/parser/*

touching parser_tokenizer.cpp, reservedwords.*, parser_expressions.cpp,
parser_functions*.cpp, parsertypes.*, and headers like base/types.h referenced
across parser modules.
github
+3



float-only (even when used by isosurface).
povray
+2



povray
Workarounds used today. Scene authors who need complex math typically emulate it

proves demand but also highlights the lack of core support.
github
+1
1) Language & type design decisions (SDL)
1.1 Choose a literal & constructor syntax (avoid ambiguity with vectors)
Vectors use <x,y,z> and are heavily overloaded throughout SDL. To keep vectors
distinct, prefer one of these explicit constructors for complex literals:

complex(a, b) → parsed as a complex literal (not a function call).
Optional: cis(theta) as a literal constructor on the unit circle (i.e., cos
θ + i sin θ).

already allow mixing with floats, and reinterpreting them would break semantics.

povray

1.2 Imaginary unit
Add a built‑in constant I (uppercase to avoid accidental collisions with
user loop variables and to match your preference for non‑lowercase
identifiers) representing 0 + 1i. This is just a constant symbol in the symbol
table (like pi is a float built‑in), backed by the new complex type. The
reserved words table lives in reservedwords.*.
github

Using I enables familiar forms like Z = 3 + 2*I; while keeping scanning simple


1.3 Operators & promotion rules
Add full arithmetic overloading for + - * / with these rules:

Promotion: real ⊕ complex → promote real to complex with zero
imaginary part; result is complex.
Unary minus: preserves type.
Equality/ordering: Keep only = and != (compare both parts with float epsilon
rules); no <, <=, >, >= for complex.

truthiness). The parser already handles boolean using float zero/non‑zero;
document the complex rule.
hws
1.4 Exponentiation
SDL does not have an ^/** operator; exponentiation uses pow(base, exponent).
Implement pow(Complex, Complex) with principal value; add overloads for
(Complex, Real), (Real, Complex). If you later want **, you must change the
grammar in parser_expressions.cpp to introduce a precedence tier (riskier; not

hws



Complex → complex: c_conj(z), c_exp(z), c_log(z), c_sqrt(z),
c_sin/c_cos/c_tan/c_asin/c_acos/c_atan, c_sinh/c_cosh/c_tanh, c_pow(z, w)
(aliases to pow).
Complex → float: re(z), im(z), abs(z) (magnitude), arg(z) (principal
argument).
Constructors: complex(a, b), cis(theta).


the parser resolve in expression context.
ijs

2) Core implementation (parser & evaluator)
2.1 New type in core headers

Add a struct Complex { DBL r, i; } (or reuse std::complex<DBL> internally and

included by parser modules. Implement basic ops and utility functions.
github
2.2 Tokenizer & reserved words

parser_tokenizer.cpp / reservedwords.*:
Reserve complex, I, re, im, abs, arg, and the c_* family as
keywords/built‑ins.
Recognize complex( as starting a complex-literal token (to differentiate from a
generic function call).
github
2.3 Parser types & value representation

Extend the value/tagged‑union used by the parser and evaluator
(parsertypes.*) to carry a Complex variant alongside Float, Vector, Color,
String, etc.

three kinds: Float, Vector, Complex (a fourth if you count Color). Keep
vectors/colors as‑is.
github
2.4 Expression evaluation

In parser_expressions.cpp implement binary/unary operator cases for complex
(including mixed real/complex with promotion).
Implement built‑ins (re, im, abs, arg, cis) and the c_ family in
parser_functions*.cpp.
Map pow dispatch based on operand types to complex exponentiation when any
operand is complex. Today pow(float,float) is wired via the float function
table; add a complex path.
github
+1
2.5 Printing & formatting

Extend str() or provide cstr(z) so #debug can print complex values intelligibly
(e.g., "(a,b)" or "a+bi"). The existing diagnostics assume float for many paths;
audit the %g style prints where needed.
github

The function system accepts float functions; it exposes a grammar and function


need one of these strategies:
povray
+1


Extend the function VM with a new COMPLEX function category (think analog of
VECTFUNCT_ID_TOKEN) and corresponding bytecode nodes supporting complex
temporaries and ops. Update the function grammar to allow complex expressions
(identifiers and temporaries of complex type). Update the compiler in fncode.cpp
accordingly.
Provide bridges so a complex function can be reduced to float via re(), im(),
abs(), arg() when the host expects float (e.g., isosurface threshold function).
This lets authors write expressions like:



Plain Text


isosurface {
  function { abs( c_sin( x + I*y ) ) - 1 }  // ok: returns float
  contained_by { box { <-2,-2,-2>, <+2,+2,+2> } }
}

cleanly.)
povray

(faster to ship)
If you want to avoid VM changes initially, add float‑returning helpers
that take float pairs (re, im) and return a float:


c_sin_re(x,y), c_sin_im(x,y), etc.
Then users can build complex flows in SDL at parse time, but inside function{}

The official functions.inc already exposes many internal helpers via

povray
+1

works everywhere the float math works today.

4) Math semantics & numerics
DBL precision: Use DBL everywhere for real/imag parts. (DBL is the
project‑wide floating type used in parser/math.)
github
pow and branch cuts: Define principal values (log z principal branch; pow(a,b) =
exp(b*log a)). Document behavior near branch cuts and zero denominators
(returning NaNs consistent with DBL and propagating through). The float function
list already includes exp, log, pow; your complex versions should mirror those
names via the c_* family to avoid changing float semantics.
ijs
Performance: Consider inlining and possibly using std::complex<DBL> internally
for correctness (argument reduction, trig identities) while presenting a simple
POD at interfaces.
5) SDL integration details
Declarations: #declare Z = complex(1,2); should assign a complex value; extend
#declare/#local rvalue typing accordingly (this directive currently accepts

docs and allow a trailing semicolon like floats/vectors.
povray

promotion.
Built‑ins already in use by vectors. SDL provides x, y, z, t, u, v as
vector built‑ins/dot items; do not repurpose them for complex components.
Use re()/im() accessors instead.
povray

grammar and precedence changes in parser_expressions.cpp that ripple across the
evaluator.
github
+1
6) Parser & symbol table updates
Symbol kinds. Add a COMPLEX_ID_TOKEN (analogous to FLOAT_ID_TOKEN,

The DeepWiki summary of symbol handling gives a good overview of where these
live.
deepwiki
Reserved words. Add new keywords into reservedwords.* and ensure the tokenizer
maps them to the right token IDs.
github
7) Standard library & examples
Include files. Provide complex.inc with macro helpers and sugar (e.g., polar
form constructor, safe printing, clipping helpers).
Samples. Ship scenes demonstrating: arithmetic, mixed real/complex math, complex
powers, Newton fractals (via isosurface using abs(f(z))), and domain coloring.
(The community POV‑Ray‑complex‑functions repo is a great
source of sample ideas; it currently implements such features at the SDL macro
level.)
github
8) Backwards compatibility & doc updates

additive.
Docs. Update:
Float/Vector Expressions chapters to mention complex and mixing rules.
User‑Defined Functions to either: (A) describe complex functions as a new
category; or (B) document the *_xy wrappers if you go with Strategy B. Current

povray
+1
9) Testing
Unit tests for arithmetic identities (z + 0 = z, z*conj(z) = abs(z)^2, edge
cases around zeros/NaNs).
Parser tests for literals, promotions, and error messages.
Render tests (scenes) that verify isosurface behavior using
complex‑powered formulas (e.g., abs( c_pow(x+I*y, 3) - 1 ) surfaces).


github

github

github

built‑in.
github

comparisons, truthiness.
github

arg, cis, c_*) and dispatch pow to complex when needed.
github

Strategy A: add complex bytecodes, temps, function kind, and grammar hooks.
Strategy B: add float wrappers c_*_xy.
deepwiki

Strategy B is used. (The shipped functions.inc sets a precedent for exposing
complex helpers if you choose wrappers.)
github


hws
+3
11) Example (proposed SDL surface syntax)
(Using uppercase identifiers and no lowercase to match your style.)




Plain Text


#declare Z1 = complex(2, 3);        // 2 + 3i
#declare Z2 = 5;                    // real
#declare Z3 = Z1 + Z2;              // (7 + 3i)
#declare Z4 = pow(Z1, complex(0.5, 0)); // sqrt as pow

#debug concat("Re(Z3)=", str(re(Z3),0,3),
              " Im(Z3)=", str(im(Z3),0,3), "\n")

isosurface{
  function { abs( c_exp( x + I*y ) - 1 ) - 0.5 }  // Strategy A
  contained_by { box { <-3,-3,-3>, <3,3,3> } }
  max_gradient 5
}
(If you ship Strategy B first, the function line becomes, e.g., c_abs_xy(
c_exp_re(x,y) - 1, c_exp_im(x,y) ) - 0.5.)
povray

12) Scope, risks, and a pragmatic path

surgery) deliver usable complex math across SDL expressions and in function{}
via _xy helpers.
Full robustness: Add Strategy A (complex value kind in VM) so complex
expressions can exist natively in function{} and be reduced to float with
abs/re/im/arg as needed.
Why not reuse vectors? Because vectors already have distinct semantics and

operator overloading, and conflating them with complex would introduce ambiguity
(e.g., is <a,b> a UV, a color slice, or a complex?).
povray
Compatibility: All additions are opt‑in; no change to existing scenes or
shipped function sets (pow, sin, etc. keep float semantics).
ijs

Repo & parser files: top‑level overview and source/parser/* modules.
github
+1
Expression grammar (float/vector) and function limitations: formal docs.
hws
+2
Function include catalog (functions.inc): pattern/internal function interface
(good model for adding complex helpers if you stage Strategy B).
github
+1
Community complex include for inspiration on naming and examples.
github

node tweaks) and a minimal complex.hpp with numerically stable c_log/c_pow you
can drop into base/, then sketch the parser_expressions.cpp switch additions.

literal like cis(theta)?


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.