Author: Filip Jaranowski under editorship of Jack Kowalski https://entropment.com Patent Pending Using the residual of the Floating-Point Unit (FPU residuum) as an alphabet for the construction of a synthetic arithmetic channel. What can be built if the divergence of the FPU from R is treated as part of the protocol? Algorithm analysis K → A_K → γ_K(M) → q_FPU(γ_K) → M̃ → ECC → M Editorial note. The text contains the raw notes of a study prepared by the "young one". We considered them worth publishing for communication and marketing reasons. Because he does not (yet, but we are doing what we can) have a highly abstract bias, he is able to explain many things understandably in language closer to natural than our nerdsplaining. He needed something to pass (school), so we gave him simple code to analyze and patted him on the back for his conclusions while pointing out what he should learn at the operational level (hence the didactic notes). The study may turn out to be much more accessible than the ones we have from the implementation. Understandable for clients and immediately explaining what this tool is for and where it sits in the pipeline. End of editorial note. Introduction and backstory. We know the basic fact that IEEE-754 represents numbers in a finite way and rounds results. Observation concerning the studied object (technical artifact - algorithm): successive operations are performed on already rounded results, so earlier operations affect the further trajectory of the computation. It is appropriate to ask how this property is used by a specific algorithm and what consequences it has for its operation. In mathematics, assumptions → reasoning → theorem / consequence is deduction. Whereas in experimental research, observation → repeatability → hypothesis / generalization → conclusion has a different epistemic status. Since the work is in the field of mathematics, I demonstrate here the property of the construction; drawing a conclusion is purely experimental and cannot be equated with mathematics. Of course, I will also add conclusions, ideas for other applications of this algorithm or its fragments or loops that can be assembled from these fragments as alternatives. The chosen research topic has interested me since childhood: How to make it so that an observer cannot easily recognize from the behavior of the game that there is a deterministic trick underneath? What seemingly insignificant detail of the deterministic mechanism leaves a trace in the observable result? Why is there residual information in the pathfinder about the generating mechanism through the preference of always the same one among equivalent-cost paths? Can I build a deterministic world that from observation will look random? The genealogy of the interest is map generator → deterministic procedure → observable behavior → hidden generating structure → problem of distinguishing behavior from RNG → pathfinder and preferences for equivalent paths → question of what the observer can reconstruct from the data → currently: what does the FPU do with information about the previous state of the computation? So I decomposed the existing mechanism (algorithm from the company, the object of study is the existing technical artifact) into operations, identified the role of the FPU, compared the ideal calculation with the machine one, studied the propagation of differences, formalized the loss of distinguishability, studied trajectories, extracted the limitations of the model, related it to the existing algorithm. The choice of the subject of the work results from the need to describe the functions of the algorithm in the simplest possible way, and if it is understandable at the school level, it fulfills this function. The school goal of the work is closed by appendices #1 and #2, and #6 contains a hint that the operation we had in school has a useful continuation. The remaining appendices serve only to indicate that the discussed operations serve something practical in the real technical artifact. From this follows the choice of the object, its vivisection, reconstruction of the mechanism, experimental verification and interpretation. Since I have already disassembled the artifact, I will see what other consequences the anatomy of this particular mechanism has. And here appears all the rest of R/~, fibers, projections, Markov, ontology of state, cryptography, HSM, Bell, deterministic world of the game with randomness not demonstrable from the inside. Of course, I consider the conclusions of applying this mechanism, for example in the form of interesting, non-invasive offline DRM and predetermined pseudorandom generators solving some known aspects of tampering with RNG. I note (digression) that the study started from a passing goal and the topic was residua in the FPU. And this part ends at the second stage out of nine. The rest resulted somewhat from the curiosity of closing the whole operation. Initially, the artifact itself "how to obtain the residuum" would have been enough for me, but that would have been suspended in a vacuum; an application was needed and it turned out to be very engaging. An additional generational motivation also arose in the company that made this algorithm available (open code). They decided that it would be very useful to them if someone without the severe occupational disease of writing everything in mathematical abstractions described the operations "humanly", that is, for popularization purposes. Reading what I wrote, they rolled their eyes and grimaced that whole paragraphs could be closed in one line describing a functor, an operator and adding a logical inequality. But as they themselves stated - no one reads that later and people complain that they don't understand what it's about. So they were very happy that someone who doesn't know how to set the head in a tape recorder to load a program would describe it freshly. Of course, a serious study exists, they did it long ago for the needs of implementation, but it is not accessible without prerequisites that I do not have. While the very assembly of these mathematical blocks into such a construction goes beyond my current competences, the very application of the blocks after analysis leads me to conclusions about the practical application mentioned in the text, because I could not restrain myself. The algorithm itself is quite niche. The modulator is a deliberately degenerated IEEE-754 calculation, the alphabet is the residuum and buckets, synchronization means the same seed and also the same toolchain, the “key” is the choice of deformation, not the door of invertibility. Therefore, one must always keep in mind that despite the names of functions, the appearances of Alice, Bob and Eve in communication, this does not concern cryptography at all, only the modulation of a signal expressed by numbers on the machine. The loop looks like a block protocol. The skeleton of the procedure seed → block → false blocks → state rotation falsely leads to the conclusion that it is cryptography, whereas seed → modulator → constellation → ECC leads us to signal modulation and that is what one should stick to when reading the text and not let oneself be led into cryptographic regions by the used function names. I will mark this in the text, because I myself was fooled. The hybrid discussed here (algorithm) does not have its own species gluing known blocks in an atypical way. It guarantees taxonomic dissonance when recomposed with a different ontology of state instead of a primitive. All component parts of the algorithm are known, the composition is an atypical turn in reasoning. Instead of hiding bits in the float word, it eats the history of the calculation. Instead of synchronizing chaos with a common signal, it synchronizes two calls of the same FPU map. It does not limit the error so that a proof in R makes sense, but lets the error go so that precisely the proof in R ceases to be the subject of consideration. There is no hard problem of inversion here, inversion does not exist from observation at all. The algorithm does not repair the automaton F, it assigns it. There is no new paradigm here. There is no new class of objects. There is nothing here to announce as novelty. It is simply an interesting application. What can be built if the divergence of the FPU from R is treated as part of the protocol? Contrary to the convention that the FPU is supposed to pretend to be R, which convention is not part of the IEEE specification but Higham's didactics (and other textbooks). New use of old blocks, interesting result, real applications, without a new class of objects. Because the semantics has been transferred from the word to the genealogy, the wrong key is a generator of synchronization, the rotation of the bunch (mutation of keys from the preimage K1) causes that the session ceases to be a function of the seed, and BER is the specification. The whole algorithm is an assault on R and spoiling benchmarks (analytics). Tools that assume F_K as a family of functions over ℝ (hill-climb, gradient, Pearson on neighboring keys) lose the object. No secret is hidden here in an equation, the object of study is not what the benchmark (analytical tools) needs. Inversion is not set by observation, there is no function Y ↦ γ. The formula is textbook and open, there is no round mixing or obfuscation here. Observation does not indicate one point, there is no difficulty in finding the point. Loss of history in q instead of a hard problem, analytics is not a cost, it is the wrong question. Analytical tools look for the slope C(K, K′) and only the fiber q⁻¹(b) is transmitted. This does not even lend itself to a security proof in models because it is a completely different class of question. We stop pretending that we are working in an algebra in which the analyst has tools. Each step of the algorithm is the inversion of what the convention in this layer expects, and it expects the erasure of the error, keeping the rotation on the manifold, the oracle is supposed to confirm, the wrong key is supposed to reject, the FPU is supposed to converge (contract) to R. //================================== What the work is about. ----------Subject of the work------------- I study the difference between calculation over ℝ and calculation performed by the finite representation of the FPU. I show experimentally how the local loss of information during roundings becomes dependent on the history of the computation. Then I show that this property can be deliberately used in a specific algorithm. The subject of the work is the deterministic FPU residuum and the way it arises. The didactic part with the discussion is needed solely so that from the level of simple, school operations one can discuss the divergence R | F (real algebra, fpu machine). The explanation and discussion of the code is an example of the whole process where this feature is used. In the work this is needed so that the work is not suspended in a theoretical vacuum, but anchored on a specific use. The analysis of the code itself and the consequences of the whole pipeline are a secondary consequence of studying a specific application exploiting in practical use the subject of the research work. The goal of the study is didactic - how to trace the operations. Purely reproductive. In the process of explaining the algorithm where there is no “cryptographic session”, but a bundle of automata on the same deformation trajectory: seed →^{Π ∘ G} Ring_n(opentext_{ the result of the operation is mapped to a finite set of representations; certain different real states fall into the same represented state; the next operation is performed already on the representation, so the earlier loss of distinguishability can affect the next transition. object → observation → reconstruction of the mechanism → formal description → verification → application => it was possible to establish that this algorithm works in this way; I understood the operation of a specific computational mechanism and established whether its residuum can be used as a stable feature. -----------Methodology------------------- there exists a specific object → we observe its behavior → we decompose the mechanism → we identify properties → we check them experimentally → we try to understand what follows from these properties The FPU has been doing this for several dozen years. I am not discovering this. I am checking how exactly what it does works, in a specific case. How the work is organized. ========================================================= Target way of organizing the study: Layer 1 is the things that a young programmer should be able to trace independently IEEE-754 and ulp; rounding; underflow/overflow; NaN/∞; order of operations; composition of operations; quaternion → octonion → sedenion; what is preserved in the representation, and what is lost; SIMD/register as an operational object; basic ECC/BER; checksum; experimental tracing of trajectories. Layer 2 is why these things together do something non-trivial Here appears: S_{n+1} = F(S_n), Y_n = q(S_n) and the most important fact: q(S_n) = q(S'_n) does not have to mean q(F(S_n)) = q(F(S'_n)). That is, the observed state is not a Markov state. This is already a bridge between programming and mathematics. Layer 3 and only here the formal apparatus Buckets, equivalence classes, fibers: F_b = {γ: q(γ) = b}. And then one can show that what earlier was a “strange glitch” has a completely precise description: many histories → one observable state. We have FPU. ↓ We perform an operation. ↓ The result cannot be written exactly. ↓ FPU writes the nearest representation. ↓ Part of the information about the previous state disappears. ↓ We do the next operation. ↓ The loss from the previous operation affects the next one. ↓ A specific error trajectory is created. ↓ The trajectory begins to look like a controlled “glitch”. ↓ We measure the result. ↓ It turns out that the error is limited. ↓ E.g. BER ≈ 2–2.5%. ↓ ECC is able to correct the remaining error. ↓ Instead of fighting the representation failure, I can use it as an element of the channel. EXPERIMENT ↓ what exactly does the FPU do? ↓ MECHANISM ↓ why is the loss of information permanent? ↓ MODEL ↓ why can different histories give the same observed state? ↓ FORMALIZATION ↓ how to describe these classes of histories? ↓ CONDITIONS ↓ when does the trajectory remain reconstructible? ↓ LIMITATIONS ↓ when does BER exceed the capabilities of ECC? # hash occurs in the text as a designation of appendices/documents specifying, explaining; ========================================================= List of additional documents with detailed explanations, code analysis, marked as #file #1_return_base_fraction.txt - function as in the name; this is the most interesting function in the whole process creating a quasi-attractor; #2_rozbieżność_R_Float.txt - detailed explanation of the divergence of the calculation, educational version; Only these two above are didactically essential for the work from the didactic side, the rest are already a discussion of the code and in point #6 the use of rotation through the hypercomplex plane. The rest are only to show that the algorithm has some application of the studied feature, i.e. the subject of the experiment exists and "lives". This is not part of the work, but only an illustration needed to demonstrate that the concept itself is not suspended in a vacuum. That is, from the second point (which has a reference in the text of the work) the presentation of the algorithm serves only that the mechanism was shown on a simple example and a real algorithm was analyzed that uses precisely this discussed property of the residuum. #3_generator_mapy.txt - function generating pathological arithmetics on the basis of #1 using the divergence of the projection F and R; #4_zwrotka_z_hardware_security_module.txt - packing the function #3 and additional (trivial) operations into an air-gapped Hardware Security Module and returning the result to the machine, part of the algorithm; #5_skąd_wziąć_buffor.txt - source of the buffer "open_text", i.e. the method of big endian encoding, trivial operation; #6_operacja_na_open_text.txt - deformation of the buffer, here enter the rotation operations on 16D, textbook, without peculiarities; numbers (for generating rotations) from #4 are used; What we learned as calculation with complex numbers and quaternions does not end with rotating a point on the plane. The same formalism can be part of a larger computational construction. And further I show how the described mechanism occurs in a real system and what happens when one starts to exploit it. #7_sprawdzenie_operacji_.txt - we add an explicit "checksum"; #8_wstrzykiwanie_bloków_fałszywych.txt - function adding false blocks indistinguishable from those containing the message; #9_rotacja_kluczy.txt - function changing the keys after discovering the correct block; #10_cała_pętla_szyfrująca.txt - we assemble the previous functions into a complete procedure for creating the message; #11_pętla deszyfrująca.txt - and its "inverse"; #12_rachunek_FPU_technikalia.txt - didactic note, absolute basics, which however one must know, and I mistakenly assumed that "everyone knows"; the note was added when in the company we went down to the bit record in the form in which it is represented in RAM memory and transferred in the pipeline to the cache on prefetch and it turned out that one must know how to write out the signal (bit) transferred by the bus; they threatened that if I don't know about it I will get a Commodore64 to learn and start from the five-byte Microsoft Binary Format, and they even threw jokes about all older formats starting from the oldest Konrad Zuse Z3 (first floating-point format) so that I would find out where the names of the characters in the Tron movies come from, and at the end also Gustafson's posit (proposal of John Gustafson and Isaac Yonemoto) so that I would know what the new concepts are; #13_specyfikacje_operacji_fpu.txt - another didactic note, which allows for an "aha" moment why in the whole process exactly such libm operations were used; it makes the process described in #2 clear from the architecture side; #List_of_sources The study itself: ------------Didactic obviousnesses------------ Obviousnesses. Reminder of commonly known properties introducing the topic. FPU is not a spoiled copy of R. FPU is a specific automaton, whose behavior we only interpret as a limited simulation of R. In case of lack of basics, one should familiarize oneself with the didactic #12_rachunek_FPU_technikalia.txt; -------------------------- 1. Construction of the representation. For a finite IEEE-754 format, the characteristic–mantissa record does not have to be understood as “a point representing exactly this number”. It can be written as a bucket, i.e. a class of values which after rounding fall into the same code: [xᵢ − (1/2) ulp(xᵢ), xᵢ + (1/2) ulp(xᵢ)) (with appropriate modifications at binary boundaries and subnormals). Then the representation operation is: ℝ → ℝ/~, where x ~ y ⇔ fl(x) = fl(y). And the element of the representation space is no longer the point x, only the equivalence class: [x]. Then “continuity” does not contain discontinuity in the sense of coverage. For finite numbers these classes/buckets arrange themselves adjacently: Iᵢ₋₁ Iᵢ Iᵢ₊₁. There does not exist a case: Iᵢ hole Iᵢ₊₁. Every real number falls into some class. Even if it is the class -infinity or +infinity, which are also equivalence classes, simply containing a bit more preimages of numbers from R. Thus ℝ is covered by IEEE representation classes and IEEE does not preserve the structure of the field ℝ, because operations on classes are not simply quotient operations with respect to this relation. -------------------------- 2. Quotient condition The quotient of the algebra needs a relation compatible with the operations: x ~ x′, y ~ y′ should imply x + y ~ x′ + y′ and xy ~ x′y′. Otherwise the result of the operation depends on which representative of the class was chosen. IEEE fails at this point as a quotient algebra ℝ/~. For example two different reals belonging to the same bucket can after an operation with another number find themselves on different sides of the bucket boundary. That is: [x] = [x′] but it can be [x + y] ≠ [x′ + y]. Then one cannot define unambiguously: [x] + [y] = [x + y]. Good observation space, but it is not an algebraic space. -------------------------- Conclusion needed for reasoning. One can look at the element not as x but as [x]ε. This is a value with resolution ε. Then instead of x = y I have x ~ε y. And instead of a point function f: x ↦ y I have a relation between classes F: [x]ε ↦ [y]ε′. Measurement semantics, not classical algebra of points. There is no point here at a distance from zero; there is a bucket into which the value fell. For the record: Bucket is a set of real values which, for a specific format and method of rounding, are mapped to the same representable floating-point value. I do not forcibly replace “bucket” with the word “abstraction class” q^(-1)(b). I would fall into formalism (which is written out, but requires much broader knowledge and reasoning) instead of showing the mechanism of the automaton. Footnote: "Interval Semantics for Standard Floating-Point Arithmetic" - Research Report DCS-323-IR Department of Computer Science, University of Victoria https://arxiv.org/pdf/0810.4196 =============================== Necessity. It is appropriate to define the object on which I am working. -------------------------- 3. Defining the object. Point model ε → 0 where we require ∃x: diam(Iₙ) → 0, Iₙ → x. Then we obtain a point. Bucket model. We never require ε = 0. We always have ε > 0 and the object is [x]ε. That is, we do not operate on a point even though the binary record pretends such an operation. We obtain continuity at the level of classes instead of points. Dedekind completeness does not cover work on an object defined in this way so adventures await us. I will treat ∞ not as a number, but as a class [+∞] = {x: x > M} for some representable maximum M. Then it is not: x = ∞ only x ∈ I∞. And then IEEE is described as a hierarchy of intervals + special boundary classes, and as “a set of points that are approximations of real numbers”. The format remains the same, the tail of the interpretation of what objects we are using changes. And in a moment we will formalize this as a change of the mathematical object on which we are working (i.e. we are not working on a point, which we already know for sure). However, this is not spoiled R, only a certain special arithmetic in certain ranges sufficiently close to R to mistakenly try to apply analytical tools from R to the study of operations. This feature will be reduced further (by operations) ad absurdum covering any other operations making tools known from R improper for analysis. Formalizing the change of the mathematical object we will start from a special case (whose property will be procedurally extended to all values) in the operation for sedenions (C-D algebra, not Clifford) because it will be needed further anyway. First presenting the assumptions and checking what options we have to choose from the rich menu of applied solutions. Classical xy = 0 asks whether there exists an exact zero point? //In memory we already hold that x =/= 0, y=/=0; for now this is not necessary, but eventually it will be. Bucket representation can ask [xy]ε = [0]ε whether the result belongs to a class indistinguishable from zero? And this is a completely different object Z₀ = {(x, y): xy = 0} vs. Zε = {(x, y): xy ∈ [0]ε}. The first is the locus of exact degeneration. The second is the locus of observable degeneration. The limit is the bit resolution of the characteristic-mantissa relation with the hidden bit for savings. Then point → interval → boundary class is not an interpretation of IEEE. It is a formal change of the object on which we perform the analysis. And the operation is performed on a finite image, while the corresponding operation in the ideal structure acts on the entire interval of possible values. -------------------------- 4. Proposal of interval arithmetic. The most direct example is interval arithmetic, the simplest presentation is: Iℝ = {[a, b]: a ≤ b, a, b ∈ ℝ}. we define [a, b] + [c, d] = [a + c, b + d], and for multiplication: [a, b][c, d] = [min(ac, ad, bc, bd), max(ac, ad, bc, bd)]. So an algebra operating on sets of values. It is not a field and does not have to be. -------------------------- 5. Proposal of an element of the domain of abstraction. The next one where intervals are treated as elements of an abstract domain. I have α: P(ℝ) → Iℝ this is the transition from the exact set of possible values to its abstract description, and γ: Iℝ → P(ℝ) this is the interpretation of the interval as a set of values. Then the program does not have to track: x = 3.141592653589793238… only x ∈ [3.14, 3.15]. And the program's operations can be performed without returning to points. Here we would be changing the type of object on which the calculation operates. -------------------------- 6. Proposal of open sets. The next one, instead of starting from points: x ∈ X and only then building open sets from them. I can reverse the perspective: an element can represent partial information about the object. For example [3, 4] can mean “I know that the value is somewhere between 3 and 4”. And: [3.14, 3.15] means more information. So I have a relation [3, 4] ⪰ [3.14, 3.15] in the sense “the first description is less precise”. Then the order is not an order of numerical values. It is an order of the amount of information. This will be useful further to explain the mechanism of the final operation itself when I reverse this cascade of precision and as a potential source we will have [-infinity +ε, +infinity-ε]. So I can have two completely different “orders”. Classical: x < y i.e. x is before y on the axis. But I can A ⪯ B i.e. A is a less precise description than B. For example [3, 4] ⪯ [3.1, 3.2] ⪯ [3.14, 3.15]. This is an order of resolution / information, not spatial. I do not store information about the number of operations as a scale of interval expansion. Then the width of the interval becomes one of the dimensions of the state: value + uncertainty instead of value so state = set of possible values This feature of the set of possible values the procedure discussed further will reduce to absurdity where from the state one will not be able to recover the exact value at all. -------------------------- Conclusion needed for reasoning. I adopted the "interpretation" of the set of possible values (interval), which is an order of the amount of information, not numerical values. In this way the result of the operation is some "semantic symbol" presented in the form of a float record. Its convergence or divergence with operations for R is only a simulation on the automaton. In practice the operations will be chosen so as to guarantee the divergence with full premeditation. Nowhere will renormalization be introduced giving contraction to R. =============================== #Step-by-step explanation of the ULP problem, numerical error, composition of operations and roundings in the external document 2_rozbieżność_R_Float; =============================== Study and reasoning about the object. 7. Kind of continuity. Continuity on sets ceases to be only a metaphor taken from Dedekind Because I have a mapping F: P(X) → P(Y) instead of f: X → Y. For a deterministic f the natural extension is F(A) = f[A] = {f(x): x ∈ A}. And with measurement/interval there is additionally the approximation A -f→ f[A] -α→ f[A]̂. So I have A → f(A) → representable description of f(A) instead of: x → fl(f(x)). The second contracts the set of possible results to a representation point, while the first preserves information about the spread. 8. Source of the semantics “the same result, different inputs” i.e. triviality and non-triviality. So I return to the special case taken from the C-D sedenion. Because here there is a semantic curiosity which step by step will explain the whole sense of the apparatus. For classical algebra Z = {x: ∃y ≠ 0, xy = 0} there exists a point locus of degeneration. For interval algebra I can instead have Zε = {X: ∃Y ∌ 0, 0 ∈ X · Y}. And in the listings (algorithm curiosity) tracking how XY changes during iteration: X₀Y₀ → X₁Y₁ → X₂Y₂ → ⋯ Then the “zero-divisor” ceases to be only a point x for which xy = 0, and indicates the existence of a region of states which under the action of the operation passes through zero / contains zero / approaches zero at a specific resolution. The symptom of this change of object (from the very fact of the operation of the fpu i.e. a specification that is 40 years old) is the “crawling” of zero divisors; they are not stationary from the very fact of the resolution of the number representation. This property occurs for every operation including those resulting in NaN (easiest to observe in the listings). The condition for bringing about such effects is the application of operations inconsistent with the axes of resolution [the bit resolution axis is one on which we can perform root extraction by a bit shift obtaining a result consistent with that obtained in R] (i.e. exactly as in 3d graphics where we stop at the quaternion, here we have two quaternions building an octonion and two octonions building a sedenion in order to reduce the number of algebraic properties used in the analysis and acquire others), preferably those without finite representation (thus irrational), and preferably transcendental. A good composition is the quotient of alternating series of the value of some number (here from the hardware security module emulator being some hash of the key) under successive roots of prime degree (then irrationality is guaranteed) in an operation with a transcendental value. The goal is to obtain values with which the fpu cannot operate in point representation — buckets are necessary and they are very extensive, and moreover mutually overlapping with the ends of the intervals (loss of resolution in the composition of operations). So value =/= point, and value = code + range of meaning + operation where (x0,x1,x2, ... x15) is not a point in space, but a sixteen-element register whose components can be permuted, masked, copied and subjected to SIMD operations. Both descriptions are correct, but operationally they give the object a different operational ontology. -------------------------- Stage summary. Key things to observe from the obviousnesses. Two different objects which I separate: ℝ from B_IEEE where the second object has a finite set of representable states and specific transition rules. The representation can be treated as classes of values ℝ/~, x ~ y ⇔ fl(x) = fl(y), but the operations do not then automatically descend to well-defined quotient operations. In particular it can be [x] = [x′] and at the same time [x + y] ≠ [x′ + y]. We do not simply get a smaller version of the same field. //$ note for Filip (field, body — strict terms for algebras, Dedekind), you must at least be able to explain a little what an algebraic field and field are, Dedekind. -------------------------- ================================================================================================ #Fragment of the code return_base_fraction, the whole has a separate external document 1_return_base_fraction.txt Fragment of code with algebraic expansion for R: pseudocode of the input value (so as not to reveal the key to the machine) from the hardware security module emulator, then transferred to the operation: INPUT: k > 0, r ∈ ℕ (even), sequence of primes p[0], p[1], … (p[0] = 3) OUTPUT: f = L / R L ← k R ← k^{1/2} // two already used m ← r / 2 for j ← 0 to m-1: L ← L + k^{1 / p[2j]} R ← R + k^{1 / p[2j+1]} f ← L / R return f //================ Scheme I₀ -f₁(K)→ I₁ -f₂(K)→ I₂ -f₃(K)→ ⋯ where the machine in each step stores some bit representative r(Iᵢ) ∈ B instead of the whole Iᵢ. The next operator acts on this representative. Thus the actual trajectory is b₀ → b₁ → b₂ → ⋯, while the corresponding set of real values becomes increasingly extensive. In 3d graphics I would consider this an error (deviation from the expected result in R) and regularly perform orthonormalization with respect to absolute axes. A single rotation can be perfectly sensible. A single scaling too. Translation too. But if I deliberately stop maintaining the orthogonality of the system and pull the composition through increasingly deformed matrices, then after a certain number of operations the representation no longer describes the same object with the accuracy that the record would suggest. I allow the FPU representation to perform successive operations in an area in which its resolution is no longer sufficient to preserve the structure of previous operations. Transcendentals are a very good tool here, because their expansion does not end on a local, short bit pattern. If I additionally interleave different bases/roots and operations with pi, I will not obtain a simple error that can be easily compensated by one coefficient. Any orthonormalization will therefore be false with respect to R, and ultimately — false beyond the range [-infinity +ε, +infinity-ε], because legal semantics also includes NaN, which in R we do not record (division by zero) as a “value”. ========================== #Fragment of the code creating the map on transcendentals from the hint in #2_rozbieżność_R_Float.txt is located in 3_generator_mapy.txt ========================== Zero divisor is thus a model example of the common pattern of loss of distinction. ab = cd, but ab/cd ≠ 1 in ℝ will of course give a contradiction ab = cd ≠ 0 ⟹ ab/cd = 1. However, after passing through the FPU representation I can have a situation (and the algorithm deliberately brings it about for every case) in which the operations q(a) ⊗ q(b) and q(c) ⊗ q(d) give the same code, even though if I had preserved the full real value of the operation (but because of the finite length of the register I cannot), the ratio of the results would not be exactly equal to 1. That is, not that “the FPU calculates wrongly”, but q(ab) = q(cd) does not imply ab = cd. And the representation projection begins to create its own equivalence relations. And then I will obtain q(ab) = q(cd) = q(1), even though semantically both histories are not histories leading to the same element of ℝ. The representational result has less distinguishability than the operation that generated it. The mechanism creating different non-trivial configurations → indistinguishable result is an analogy of the known non-trivial zero in the zero divisor, where the record in both cases for R says zero. Only under R do we erase the history only for this one case; here the algorithm guarantees its erasure for every case in exactly the same way only by operating deliberate degeneration of the distinguishability of intervals represented by floating-point numbers. In R the stationarity of zero divisors comes from an excessively rigid algebraic construction. constant space + constant operations + exact equality The algebra does not possess a mechanism Z(x) -> Z(x+delta) as part of its own dynamics. The machine possesses this property because of limited resolution. I will specify step by step where the factor not transferred in the record occurs. I have two different states x_triv ≠ x_nontriv and both after representational projection give the same state: q(F(x_triv)) = q(F(x_nontriv)). But their trajectories were different: x_triv → ⋯ → b and x_nontriv → ⋯ → b. The final b does not contain information allowing these two histories to be distinguished. In an ordinary discrete function this sounds like F(x₁) = F(x₂). Whereas here q(F_γ₁(x₁)) = q(F_γ₂(x₂)), γ₁ ≠ γ₂, where γ means the history/cascade of operations. That is, the information about the distinction was not “hidden in the output”. It was physically lost through the representation projection. The bits went into the radiator and departed with the fan noise. Which causes that the assumption that if the input is exactly an n-bit word, there exist at most 2^n different inputs does not describe the object that is executed here. For example I have 2ⁿ inputs, but an enormous number of different computational histories associated with different key-dependent operators, and at the end significantly fewer buckets |H| ≫ |B_obs|. This does not mean that I have more than 2ⁿ inputs. It means that the space of computational histories is not the space of input states. Different elements can be reduced to the same observable result, even though their meaning results from a different path of arrival. Which is an analogy of the semantics of the zero divisor. An analogy, not an isomorphism, because we operate here on the continuity of intervals, not on R. Moreover, as the representation is deformed by successive operations these intervals begin to have asymmetric boundaries dependent on the history of operations, until the bit distinguishability is exhausted and the interval for every history is the full range of the fpu register. We obtain a construction in which through sufficiently long composition of transcendental operators collision becomes a locally common property of the representation: ∀b ∈ B, ∃ γ₁ ≠ γ₂: q(F_γ₁(x₁)) = q(F_γ₂(x₂)) = b. And the whole bucket q⁻¹(b) can contain a very large family of histories (x, γ). Then the classical problem “find that one key which gives this result” disappears. There appears “find any history from the enormous family of histories whose projection falls into the same bucket”. The key K does not then have to be “a secret inverting the function”. K is the seed of the generator of a specific pathological trajectory: K ↦ γ_K ↦ F_γ_K. And thus: F_γ_K(x) is not one function belonging to a fixed family of known structure. For each block a different local object is created: F_{K,b}. Its construction depends on information that is not the content of the encrypted block. There is therefore no natural search “here is the family of functions F_K, now I will examine its gradient with respect to K”. First one must establish what object is being examined at all. If this object is constructed only during iteration, the gradient may not be “small” or “large”. The gradient is not the proper mathematical object. The key is thus the seed of the generator of pathological arithmetic needed exclusively for this one block. // And the next key K1, unused and therefore unknown, serves for its erroneous opening in order to obtain numbers indistinguishable from random in order to mutate the whole bunch of keys after each block. However this is already a detail of the protocol, not the interesting property that I am discussing. Analytical issue of HillClimbing. Assume that there exists some analogous correlation function C(K, K′). Normal hill climbing requires that a small-step change of the key gives a statistically useful change of the result: K′ ≈ K ⇒ C(K, K′) ≈ C(K, K_true). But if the maximum has a width smaller than the resolution that the attacker can represent, I will get something like ΔK_peak < 1 bit. Then in the discrete space of keys there does not exist a practically useful “slope”. Not mountain → slope → valley, only valley | microscopic peak | valley. And this is a correlation property resulting from the loss of information, not “high cryptographic entropy”. In the algorithm this is obtained by the number of operations depending directly on the length of the key (1:1). In this way the hill is squeezed below bit resolution, and the valley is pressed below the bit resolution of zero. That is, the correlation for the whole valley is (0-e,0+e) (Pearson is powerless here) except for the peak of the proper key. By controlling the number of operations one can arbitrarily widen this hill, i.e. the analytical function operates on this completely standardly, but the information about it is narrowed below resolution by the number of operations leading to uselessness because of the representation. That is, the composition of a series of analyzable operations through degeneration of preimages onto the image leads to an unanalyzable representation. If I observe Y = q(F(X)), and q is a projection that has erased information, then there is no obligation of the existence of F⁻¹(Y). There may exist an enormous number of preimages: F⁻¹(Y) = {X₁, X₂, …}, or there may not exist any exact preimage for a specific representative. And if the information has been physically removed by underflow/overflow, there is no mathematical procedure that will recover it. One can try to estimate. One can try to impose priors. One can try to use an additional channel. But one cannot analytically “invert” information that is already no longer in the observation. This is exactly the difference between hard-to-invert (hard problem) and informationally irreversible. In this case one cannot invert this process even with the correct key. But the correct key allows inversion of a sufficiently good approximation (BER max ca 2.5%). BER is of course controllable in the algorithm by the number of operations and is directly correlated with the possibility of analytical examination of the distribution. The deliberate setting of the number of operations, loss of ULP and BER are set beyond the threshold of transferable representation; this is even revealed in the code in the form of a switch command, where the choice of representation of the mantissa length occurs, although set rigidly to 52+1, but other variants of mantissa length (and key) were also considered and included as potential options. sequence of operators→increasing deformation of the representation→FPU projection→loss of history→massive identification of different trajectories We are therefore in signal theory, not cryptography. Although operationally one can be fooled, mathematically this is strictly distinguishable. There is no “key to invert the function”. There is a parameter selecting one of the families of trajectories that the machine will generate. And deciding which relations it preserves, which it destroys, which new identifications of elements it causes and which information remains encoded exclusively in the history of the trajectory. “Error” and the pathology of arithmetic is here a misleading category, but needed for the transition in reasoning from “error of F with respect to F” to “this is how it works and it cannot be otherwise because it is not R”. In the standard numerical model x ↦ q(x) is treated as an approximation of x. But nothing mathematical forces us to interpret q as “a bad version of the true x”. One can equally well treat it as an operator of change of representation semantics. Then IEEE is not “inaccurate ℝ”, only a specific automaton: B -f₁→ B -f₂→ B -f₃→ B … with a very special relation of this automaton to ℝ. We take a property regarded for decades as an artifact of numerical error and deliberately set the dynamics so that this artifact dominates over the original algebraic structure. From the loss of bits the main construction operator arises. I have a projection q: H → B, where H is the space of histories. Then for a given result b, q⁻¹(b) is not one state, but a fiber: F_b = {γ ∈ H: q(γ) = b}. And if the composition of operations causes that for practically every b |F_b| ≫ 1 then the information about the origin of the state has ceased to be a function of the final state. This is a very interesting property. The final state states “I am in bucket b”. It does not however carry “I am in bucket b because I traversed the trajectory γ”. Semantics is therefore in the trajectory, not in the final state. If for a 53-bit key I have N = 52 operations, because one bit is structurally “hidden” and its manipulation would lead to the trivial 1, this is not like arbitrary increasing of the number of rounds but matching the length of the cascade of operations to the resolution of the representation. //Technically this reduced to finding such a cascade which in “one operation” degenerates the representation by at least one bit relative to that expected in R and leads in iterations to the divergence of a function which in R converges to 1. That is, such an operation whose representation on the fpu guarantees counter-contraction with respect to R. Such an extreme kind of numerical error dependent on the input parameter. So N_critical ~ number of useful bits of the representation. And below this threshold a noticeable correlation structure remains K → K ± 1 still gives information. Above, the appropriate information is successively eaten by successive projections. Then the number of operations is not a parameter of “strength”. It is a parameter of the degree of loss of distinguishability. Key as seed of arithmetic. Causes that the scheme does not look like F_K(x) = fixed function with parameter K. Only rather K -generator→ A_K and x -A_K→ γ_{x,K} -q→ b. The key generates local arithmetic, and only this arithmetic generates the trajectory. That is why the analytical tools (benchmarks) began to fall apart. Tools assume that there exists some object F_K which can be examined as a family of functions {F_K}_K. Meanwhile here we have K → A_K → trajectory → projection → bucket. If A_K is generated during the performance of the operations, then the “family of functions” is already a secondary description. The primary object is the trajectory automaton. If therefore the work began from identifying when correlations cease to work, and then the properties that enabled classical analysis were removed, the final object does not have to be a cipher. It is an operator of signal degradation with seed-controlled dynamics. Cryptography can be one of the ways of using such a mechanism, but it does not define the mechanism. Observation: R-ary structure ≢ structure of FPU execution. Structure of FPU execution = value + quantization + order + history of loss of information. By consciously designing operations so that the history of loss of information dominates over the information preserved in the current bit word, we obtain an object whose natural description is no longer a function on ℝ. It is here that “continuity on sets”, buckets and zero-divisor begin to compose into one picture. The state of the machine is too small to contain the semantics of the process that generated it. This is not a problem of a hidden variable (secret), because nothing needs to be added to the current state as an additional variable. It is dependence after histories. I have a bit state bᵢ, but its meaning is not a function solely of bᵢ: meaning(bᵢ) ≠ g(bᵢ) only meaning(bᵢ) = g(bᵢ, γᵢ), where γᵢ is the history of the trajectory. Except that γᵢ does not have to be stored. It is deterministically generated by the seed K → γ_K. So from the point of view of the process K → γ_K, x → F_γ_K(x). The seed is therefore a kind of generator of hidden correlational structure, not a container for additional information about a specific result. And then a very important difference appears when we have two inputs x₁, x₂ it does not have to be that x₁ → y₁, x₂ → y₂ and later by chance y₁ = y₂. It can be that the same generated arithmetic A_K causes that: x₁ -A_K→ b₁, x₂ -A_K→ b₂, and the correlation between b₁, b₂ is preserved precisely because both were subjected to the same pathological dynamics. That is: corr(x₁, x₂) → corr(b₁, b₂) not because the information about the relation was precisely transmitted through successive operations, but because both signals were generated by the same forcing system. If two processes have a common seed: K → A_K then they can be correlated without one transmitting information to the other. In the causal sense x₁ ← K → x₂ can generate correlation between observations, even though there is no channel x₁ → x₂ K does not generate common noise. It generates the structure of the operator through which all data pass. We have simultaneously loss of information about the history and lack of total divergence of results (BER max ca 2.5%). At first glance one could expect that since the machine destroys information, all results should become random. But no. The loss of information is not random. It is conditioned by the generated trajectory: K → {places of overflow, underflow, NaN, roundings, transcendental deformations}. This creates a deformation field in the space of computations. Two different inputs can lose a great deal of their own information, and yet remain similarly deformed, because they passed through the same “landscape”. That is, the preserved correlation does not have to be a transfer of input information. It can be correlation induced by common dynamics. There is no hidden value to recover. There is a hidden genealogy of the state. The seed does not store the lost bits; it determines which bits will be lost and in what order. Of course for given information. I will consider this from the side of determinism, because the conclusions from it will be needed for the conclusion: Will mobs examining the “world of the game” from the inside establish on the basis of available representations that it is “fundamentally random” (whatever that would mean) or deterministic? And going further → will the player also fall for it (suspending disbelief that after all it is realized on a computer, so underneath there is a deterministic mechanism)? The problem of statistical independence in the Bell sense, superdeterminism and its consequences for the interpretation of correlation as transfer of information. Here we have Mary in an open, small and experimentally accessible version. We have K → A_K and then many observations x₁ → y₁, x₂ → y₂, … A common K generates correlations between the results, although no result has to carry information to the other result. An observer who does not see K or the full trajectory sees only P(y₁, y₂ | x₁, x₂). And from this distribution alone he is not necessarily able to decide: random common factor vs. deterministic common factor outside the horizon (or resolution) of observation. In the standard setup we have a hidden variable λ, measurement settings a, b and results A, B. A local hidden-variable model would require A = A(a, λ), B = B(b, λ). To derive Bell’s inequality, however, one needs among other things the assumption of independence of the settings from the hidden variable P(λ | a, b) = P(λ). The superdeterministic possibility P(λ | a, b) ≠ P(λ). Not because λ “sent a message” to the apparatus, but because the settings of the apparatus and the state of the system have a common, deterministic history. Here we have an illustration of this mechanism. The observed correlation is not in itself proof of information flow between the observed objects. It can be the effect of a common generator of structure. One can even attempt a controlled experiment: 1. I choose the seed K; 2. K generates the arithmetic; 3. the arithmetic destroys part of the information; 4. it leaves a specific pattern of correlation; 5. the observer receives only the final buckets; 6. the observer has no access to the genealogy of the results. The FPU sent the data into the radiator. Then I have two sources to present: randomness → Y and K → A_K → Y and construct them so that from the perspective of available observables they are indistinguishable or practically indistinguishable. This is a very concrete example of observational equivalence of models with different ontologies of the process. It follows from this that a finite observer cannot always distinguish randomness from deterministic dynamics with inaccessible history from observation alone. This algorithm is a constructive, laboratory example of this indistinguishability. It does not require postulates. I know exactly where the seed is, I know what operations I performed, I know where the information was lost, and yet an observer deprived of this channel cannot reconstruct the genealogy from the final state. Which leads to the question: How do we know that the observed “randomness” is randomness, and not deterministic correlation with a part of the state of the world which the experiment by definition does not observe? The considerations and analyses included in this point have an applicative character concerning the conclusion that I reveal at the end. It concerns determinism in the game and DRM applications. The starting point is of course normal physics and quite normal experimental questions, which may sound too “philosophical”, but these are questions that arise by reason of the application of the mathematical apparatus. Wrap — wrapping of the space of results (cyclicity in the limit of a 53-bit seed). In this version of the experiment (opening with a “faulty” key applied in the procedure for continuous change of keys) there appears a very clean separation between the determinism of the mechanism and predictability from the available observer. I have two systems: K₀^(A) = K₀^(B) and the same stream of messages mᵢ. Thus deterministically: Kᵢ^(A) = Kᵢ^(B) or at least they remain on the same trajectory. The third system has however: K₀^(C) = K₀^(A) + δ, where δ changes one bit. After one iteration we obtain d(K₁^(A), K₁^(C)) ≈ d_max. And this is not the ordinary “small change of input → slightly larger change of output”. It is the opposite: microscopic difference of the initial state → macroscopic difference of the observed state. And because the space of keys is cyclic, I can have K → K + 1 but F(K + 1) lands practically on the opposite side of the space. And then “randomness” is a property of observation, not of the mechanism. I know that K_{n+1} = F(K_n, m_n) is completely deterministic. I even know exactly what F is. And yet an observer who does not know K₀, after the first step no longer has practically useful information about where K₀ was located in the space. After successive steps K₀ → K₁ → K₂ → ⋯ his observation can look like noise → noise → noise. Not because the generator is random, but because the information needed to reconstruct the trajectory has been cut off from the observed state. This is an example of the difference deterministic ≠ predictive for a given observer. The very fact that the next state looks independent of the previous one does not yet decide whether we have a random source or deterministic dynamics with an inaccessible initial state. In a representation with finite resolution the question whether we are dealing with randomness or with determinism outside observation remains impossible to decide. If the observer knew only K_n and observed K_{n+1}, but did not know the generator F or K₀, he could empirically state that he finds no local dependence allowing him to predict the next state. He could even build a very convincing statistical model: P(K_{n+1} | K_n) ≈ wide distribution. Meanwhile “ontologically” the situation is banal: K_{n+1} = F(K_n, m_n, K_hidden). There is no randomness. The problem is that the observer does not have the proper state variables. And he cannot have them, because they are not within the range of observation; they were long ago converted by the fpu into heat, the radiator passed them to the atmosphere, and the fan mixed the atmosphere. This is a process rather difficult to reverse. The observer tries to describe the system by means of a projection of the state which is not a Markov state for the observed dynamics. After projection one can deliberately lose the Markov property, where the full system S_{n+1} = F(S_n) is deterministic and Markovian. But I observe only Y_n = q(S_n). Then in general there does not exist a function G: Y_n ↦ Y_{n+1} such that Y_{n+1} = G(Y_n). Because there can exist: q(S_n) = q(S'_n), but q(F(S_n)) ≠ q(F(S'_n)). That is, two states identical observationally have different futures. Then the observed process looks random, even though the full process is deterministic. History is part of the dynamic state, but not part of the observation. And the FPU supplies here a physical, very concrete way of performing the projection q. With resolution there appears a very interesting trap. If I increase the resolution of the representation, I obtain successively: q₈, q₁₆, q₃₂, q₅₃, q₆₄, … and through a certain range the behavior can look more and more like a smooth function over ℝ. I can observe Δx → 0 ⇒ ΔF(x) → 0 in the range of resolution which does not yet see the catastrophic loss of history. This gives an appearance of continuity. But if the proper object is the finite representation, then with a sufficiently large number of iterations (or a deliberately designed operation which the automaton is certainly not able to carry out in the returned resolution) I can enter an area where: resolution < information required by the trajectory. Then there appears a sudden transition apparent continuity → deformation → collisions → loss of history. So “continuity” can be a scale effect of observation. On a certain scale it is an excellent approximation. On another scale it turns out to be an artifact of the projection. “Continuity” in the sense of a floating-point number after an operation does not satisfy the definition from R. This is not a field, there is no field, supremum does not occur. Deterministically the operations have continuity (the result exists and is always the same result), but this is not the kind of continuity that is needed in a “well-behaved” algebra, for whose objects we have constructed analytical apparatus. Deterministic dynamics of the full system -projection→ apparently random dynamics where the projection does not have to be an abstract decision of a mathematician. It can be a physical limitation of the computational apparatus (or observational, or recording, or all at once). Therefore this proves nothing about physical reality. But it very well shows the logical weakness of the inference: “We cannot predict the next state from the observed state, therefore the mechanism contains randomness.” //And immediately like a devil out of a box the probabilist jumps out who will tame the results with statistics — rant, in the absence of full representation which was destroyed in the fpu process one cannot apply proofs from R here. This conclusion must not be drawn without the additional assumption that the observed state is a sufficient dynamic state of the system. The algorithm shows that one can build a fully deterministic, completely known mechanism in which this assumption is explicitly false. I gave the conclusion with Bell in the background in order to present a laboratory example of a visible generator and truncated observables. Superdeterminism in Bell is the same gap without a guarantee that the generator exists at all. The laboratory shows the inferential gap. I do not touch ontology. Examination of Y alone of this pair does not decide. Access to S in Bell is not given. Here we have a didactic exception, where S exists and Y lies about S. Therefore the use of the word “random” in any text requires defining with respect to what we called it that way (this word has no absolute definition by itself). It is always worth determining whether with respect to prediction from Y_n (epistemic), or with respect to the model P(Y) (stochastic), or with respect to the full state S (ontic and here we usually do not know whether we can know at all because superdeterminism always lurks in the background) and with respect to the length of the description (algorithmic). Calling something randomness is very problematic; in the computer we do not have this problem — here it is certainly determinism, at most nicely hidden. ----------------------------------------------------------------------------- And here the conclusion and DRM application: having a deterministic (on the computer we realize this) generator of trajectories of “spoiling” the calculation we obtain a result that is analytically untearable. Because we have a division into random | deterministic, and the world preserves some coherence of behaviors (low BER, quantum tunneling happens, but does not dominate) then most probably the very definition of “randomness” is from the point of view of the observer (and intersubjectively of observers) of the representation, and determinism is the objective state of the mechanism of realization. This allows the use of the very “decrypting” mechanism of blocks, and even extraction from them using the K1 mechanism of numbers which will introduce seed-dependent buffers into the program pipeline setting certain variables in the “game”. Which for example allows distribution of the entire gameplay with differences dependent on the key used for the game (DRM), so that each generation will be slightly different (it will have “character”), and even a mechanism which at key points of the gameplay will take values from this mechanism and without spoiling the game itself will lead to absurdity in the case when the process fails (DRM violation) giving quite absurd inputs. Let us assume that there is a value there setting the cooldown frequency of an ability of some boss/miniboss or its HP. It may turn out that this value after using a “wrong” key will be ridiculously low (low HP) and the boss will not turn out to be an obstacle, but a negative value of the ability cooldown will spam the player with an impassable confrontation. Without spoiling the game the pirate version will turn out at some point to be unplayable further. Which can affect the cost of distribution, because the demo can be the full game, but unplayable because of this absurdity after some challenge, while distribution of the key itself can be cheap. I am considering the utilities of such a mechanism; it goes significantly beyond the scope covered in the apparatus itself by the creators. Especially the mechanism of the K1 operation in #9_rotacja_kluczy.txt ----------------------------------------------------------------------------- In the Shannon model the key is a hidden state which the observer does not know. For the cipher C = E_K(M) the adversary knows C and the model E, but does not know K. The whole construction of the problem consists in the fact that there exists a certain secret which one must find out or about which one must eliminate uncertainty. Therefore natural is the test: K′ =? K or its practical equivalent D_{K′}(C) =? sensible plaintext. This is precisely what gives the adversary an oracle deciding whether the candidate passed / did not pass. But if after the construction I have q(F_{K₁}(M)) = q(F_{K₂}(M)) for very many K₁, K₂, then finding a key which gives the observed bucket does not identify the secret. I have a set K_C = {K: q(F_K(M)) = C}. And it can be |K_C| ≫ 1. Then the answer of the oracle “is K′ consistent with the observation?” sounds yes for an enormous part of the space. This is fundamentally different from the situation in which there exists a narrow peak C(K′) ≈ C_max around one K. Here we obtain observation → a great fiber of consistent histories. And then the oracle “have I solved it?” loses its selective function. The oracle can be correct, and yet useless — “every answer is good, only rather not that one”. Assume that the oracle really answers correctly: O(K′) = 1 | K′ ∈ K_C 0 | K′ ∉ K_C. If K_C is enormous, the oracle works ideally, but does not solve the identification of the key. And if additionally different elements of K_C lead to different histories: K₁ → γ₁ → C, K₂ → γ₂ → C, then the oracle confirms consistency without the possibility of saying which genealogy was real. This is more important than an ordinary collision of functions. Classically I want: secret → unambiguous effect for the holder of the key, while the adversary tries to find the secret through observation of the effect. Here K → A_K → γ_K → q(γ_K) and the projection q destroys the information about the genealogy. So the observation does not say “here is the encrypted version of the secret”. Only “here is the bucket into which very numerous histories fall”. And this really undermines the sense of asking the question “which of these keys is the true one?” on the basis of this observable result alone. ----------------------------------------------------------------------------- And here another applicative conclusion. Let us assume that we want to avoid save-spamming until the result expected by the player is obtained as a result of the action of the RNG. If we use the discussed “encrypting” algorithm (signal modulation) as a generator of determined pseudorandom numbers and we save the sequence then an instance (say a controlled unit in 4X) decoding it with its own key will always obtain the same number from the already accepted sequence; this causes that despite the appearance of randomness the result of the event is determined and save-spamming becomes pointless. Alternatively the very method of “encryption” with this algorithm is a very good application for RNG. One can prepare blocks and recover different numbers from them with different keys. That is, assignment of some key to instances determines what they obtain from a given block, say “global for the turn”, i.e. determinism of behavior, reproducibility despite randomness. Moreover if one applies the whole mechanism in a loop then the entire gameplay becomes superdeterministic while still looking random and having a reproducible trajectory. Just as we normally use a seed for map generation, here we can generate a trajectory of events satisfying the statistical appearance of noise. And the feature of precisely this algorithm is all the more important that such a set of blocks prepared in advance can be listed, viewed with a hex editor or anything, and without confrontation with the key of the entity that will use it nothing at all follows from it. What is more — forging such a multi-use block (for different entities) allows establishing the expected result with knowledge of the key of one entity, but for others this number will be different from the same block because of a different key and one cannot analytically correlate this in any practical way other than iterating possible values of the blocks with respect to the keys. ----------------------------------------------------------------------------- We move on to the discussion of the procedure. That is, already outside the algorithm. The transmitted message contains explicit checksums (true for the correct key) and modulated information. Block by block. Each block has a different key. Each true block in an operation on an unused key (an element of the process implementation, trivial) gives some numbers (as indicated above, indistinguishable from random), which change the whole bunch of keys. The message contains false blocks with true checksums arising in such a way that it is checked so that the checksum definitely does not match the current opening key. The proportion of these blocks to the true ones is set in the application by a slider (depending on the level of the user’s paranoia). Because the checksum is trivial, very many keys satisfy it, therefore a checking procedure is performed immediately on the sender’s machine and if by chance a false block has a checksum consistent with that for the true key, it is not used (one can draw another key for the false block, a matter of settings). “Cryptographic undecidability” is here a property of the observational channel, not an element of the construction. I have three different operations which I did not previously separate: generation of arithmetic → degradation of the signal → reconstruction of information The seed does not invert the signal, it only selects the proper degradation If the correct seed gives BER ≲ 2.5%, then I do not have D_K(E_K(M)) = M. But D_K(E_K(M)) = M̃, d_BER(M̃, M) ≲ 0.025, and only the post-process: R(M̃) = M. Which is not part of either this algorithm or the process; for this one must use a method of creating the message and its recovery, for example by Solomon. And the buffer processed by Solomon can already contain a normal ciphertext (for institutional reasons, certification etc. the channel does not meet the requirements imposed on ciphers, it cannot be compared to them even if from the calculational side it gives such an appearance; the channel ensures full deniability and absolutely does not ensure any identification, signature etc.). That is, “decryption” is in essence demodulation + error correction, not algebraic inversion of the operator. This greatly changes the role of the key. The key does not say how to recover the lost bits. Only what generator/deformation was applied in order to obtain precisely this stream with a specific error. This is communication through a channel with controlled error. Then the checksum is not an oracle “have I found the key”. I have a block with an explicit consistency condition, but a candidate without the proper key gives something that can look locally sensible, however it does not satisfy the control relation of the block. The correct key on the other hand gives checksum(M̃) = checksum_transmitted. But if there exist many seeds giving the same or sufficiently similar bucket, then the checksum does not necessarily identify the seed. It only identifies an admissible result of reconstruction. Oracle of message correctness ≠ oracle of history identification. The emulator “omnipotent Eve, adversary” is precisely a test of this difference. If the adversary receives during generation all the keys used by the system, and yet does not recover information about those keys which the user does not possess, this demonstrates that knowledge of part of the history is not sufficient for reconstruction of the missing fibers, because each missing key generates its own local arithmetic. False blocks additionally destroy the significance of local success. If part of the blocks is deliberately false, then a single local success “this candidate gave a sensible block” does not yet mean “I have found the proper trajectory of the whole stream”. And since the information is not semantically bound to the position of the block, but is later assembled from an ordered buffer, attacking the order of the blocks as a carrier of information is also not the proper model. This is another case of the same principle: observable local structure does not have to be informational structure. A block can be calculationally correct, but have no value as a hint of genealogy. Because to decode a block it is necessary to carry out all bitN operations, which are the quotient of alternating series under a root of prime degree, the weight of the operations is significant. Because one must reconstruct the degeneration of the arithmetic for the seed, and since it is degenerated there are no shortcuts here. Therefore iteration over keys (brute force) imposes an enormous cost despite 53 bits of key per block. Then “the longer the message, the harder” has a very concrete cause. In cryptography this would be a paradox. But here the resistance results from the length of the message, not from the key, which is only a generator. And of course the first blocks can be as false as possible. It is not only about accumulation of BER. If I have N independently degraded blocks, then a candidate must pass through an ever greater number of conditions: C₁, C₂, …, C_N. If false paths pass each condition with probability p < 1, then for a random history P(passing all) ~ pⁿ. Whereas the true seed does not have to reconstruct the original bit-by-bit. It must generate a signal lying in the tolerance area of the channel, after which the correction code does the rest. This is a different problem from find the exact K. Find a trajectory belonging to the proper observational fiber. Many different histories → the same observable signal and therefore the question “which history was the original?” is deprived of an answer from the available channel. The checksum can answer “this reconstruction is consistent”. It cannot answer “this reconstruction arose from this specific seed”. Loss of genealogy is a feature of the channel, not the lack of a sufficiently good inverting algorithm. Deniability is an irremovable feature. The purest description of the whole mechanism is K → A_K → γ_K(M) → q_FPU(γ_K) → M̃ → ECC → M where only the holder of the proper K must know the proper deformation, but even he does not recover the history from the FPU, he recovers only a sufficiently good signal. This is closer to a communication channel with controlled, seed-dependent degradation than to a cipher. And “undecidability” appears then as a consequence of the fact that the observational channel preserves information sufficient for reconstruction of the message, but does not preserve information sufficient for reconstruction of the genealogy that generated this message. “Channel” is the purest description of this object. One can separate it completely from encoding: M -arbitrary encoding→ X -C_K = seed(opentext)→ Y -arbitrary decoding/ECC→ X The algorithm itself does not say what X is. For it this is simply an input stream. It can be plaintext, ciphertext from AES, a compressed file, a Reed–Solomon codeword, any binary stream. And on the other side one can do Y → ECC → X and only then, if X was a ciphertext, perform the normal X -K_crypto→ M. That is, the mechanism does not compete with cryptography as “another cipher”. It is a channel layer. Then an interesting asymmetry appears. The simplest brute force really exists: K₁, K₂, …, K_N and for each candidate one can pass the message through the channel and check whether the result gives a correct buffer/ECC/checksum. But the cost is no longer only O(|K|). If the correctness of the candidate must be established on the whole message, it becomes rather something like O(|K| L), where L is the length of the message, and in the construction there is additionally the cost of generating/tracing the trajectory itself. False blocks are part of the cost of the attacker’s experiment, although for the recipient they are cheap to reject. The recipient does block → channel⁻¹_approx → checksum and fail ⇒ skip. The attacker, if he iterates over seeds, receives however not only the true blocks, but also all the artifacts generated by wrong seeds. He must classify them. This gives a quite characteristic situation: recipient: many false blocks = rejection; attacker: many false blocks = space of candidates to check Since the channel does not have to be invertible, there is no need for its “inverse” to be a function. It suffices C_K(X) = Y and a reconstruction procedure R_K(Y) = X′ such that d(X′, X) < ε and ECC does the rest, i.e. ECC(X′) = X. This is a fundamentally different model from the classical D_K(E_K(X)) = X. Here R_K(C_K(X)) ≈ X and the accuracy of the channel is determined not by algebraic invertibility, but by whether the degradation remains within the reach of reconstruction. “The correct seed also does not invert the operations” ceases to be a paradox. There is nothing to invert. The seed selects a realization of the channel which preserves enough structure of the signal to reconstruct the data. Whereas encryption before this channel can really be completely normal: M -E_{K_c}→ X -C_{K_s}→ Y -R_{K_s}+ECC→ X -D_{K_c}→ M Then the channel does not even have to “know” that it is carrying ciphertext. And this is probably the most precise separation of these two things. With a sufficiently effective channel cryptography becomes an “ornament”, which can be formulated technically that the channel can provide observational separation of histories greater than that which the proper cryptographic mechanism needs to hide the content. Cryptography can still have (because the channel will not do this, it does not even have such a possibility) significance for authentication, integrity and the threat model, but it ceases to be the only mechanism hindering reconstruction. “Its from bits” does not construct a secret from mathematical irreversibility of a function, it only produces a channel in which finite observation does not preserve the genealogy of the process. Reed–Solomon is not an addition to the “cipher”, but an element of the channel specification, because since the channel has a known, boundary BER, one must choose a correction code so that its correction area covers this BER with an appropriate margin. And precisely for this reason the possibility of inserting into it an ordinary ciphertext is a very practical transitional property. The existing ciphertext remains a ciphertext. We change the transport layer, not the trust model. That is, the enterprise has for audit purposes AES-256 / an existing standard, and underneath encoding → QCO → ECC One does not have to convince the organization that it should throw out the whole cryptographic stack and trust mathematics. This is an enormous implementation difference. The channel is a layer of protection/transformation of the transported stream. Then standard ciphertext ⊂ channel and for the security department the situation is psychologically very convenient “Inside there is still our certified ciphertext.”. Even if from the calculational side the ciphertext is only “a thin sheet inserted into an armored envelope”, even if some analytical solution for ciphers existed, one cannot get to the ciphertext in a reasonable time. This is not “a cipher which happens to be strange”. This is a channel which does not meet the basic conditions to sensibly call it a cryptosystem: lack of secret, lack of hard problem, lack of inverse function, lack of oracle identifying history, lack of signature, lack of certificate, full deniability. Especially the lack of invertibility even in the ideal model distinguishes this from a “weak cipher”. A weak cipher is still conceived as an invertible transformation; only someone found a way to limit the search space. And here X → Y is a mapping with loss of information and it does not matter how much computational power the observer receives, if the information has been folded by the projection into the same observational fiber, there does not exist a function Y → X recovering the original history. This is the difference between “we do not yet know how to invert” and “inversion is not determined by the observation”. If the whole problem results from the fact that I have invertible F, one can stop constructing invertible F. This changes the class of the problem. Instead of looking for a better representation of the operator one can destroy its invertibility. Formally F_K: X → X I replace with C_K: X → Y where |C_K⁻¹(y)| > 1 And this not because the opponent does not know K, but because the channel itself performed the identification of many states. Cryptography: make it difficult to find the proper point; channel: make it so that the observation does not determine one point. Instead of having in the forest a tree with a hard-to-find ribbon “here is the treasure” we tie explicit ribbons “here is the treasure” on every tree. These are two different informational operations. The FPU is a specific automaton, whose behavior is only interpreted as a simulation of R, and not a spoiled copy of R. I took the automaton, performed the operations, measured the trajectory, compared it with real arithmetic, determined the range of degradation and checked whether the result can still be reconstructed through ECC. I investigated why the properties are precisely such. Summary of the algorithm: Transmitter and receivers are entangled by the seed. The channel carries the projection, not the original. The block key is a honeypot. The bunch is a trajectory. The readout is lossy and sufficient. There is no oracle, because there cannot be one. ----------------------------------------------------------------------------- Conclusion about practical application 1: We have two (identical with respect to architecture and uops) systems, and each of them has an attached hardware security module (as in the emulator in the code) containing the same bunches of keys. Let us assume that for ordering one bunch is for a given system transmitting, the other receiving (this is not necessary, one can operate on one, but it requires more operation overhead in case interruptions in transmission occur and one must restore from the HSM the last stable state). Communication between the systems lasts constantly (there exists continuous transmission of false blocks). The systems check them and skip them. When true blocks are sent they are not skipped. K1 is activated and the systems begin to mutate the bunches of keys. In the code there is even a comment that the ineffective activation of the K1 operation should always be launched (copy-paste operation), but result in mutation of the proper bunch of keys in the HSM only when the block was not rejected. In case of a side-channel attack under such a solution. That is, we constantly have transmitted noise, but when this noise “is not senseless” (there is no reason to skip) a message buffer is generated, Solomon composes correctness (BER issue), the buffer already contains a normal, certified ciphertext and its confirmation qualifies the acceptance of the change (mutation) of the keys as having occurred. This gives a very good covering channel, because we have deniability “it is not known whether we are precisely transmitting a message, the noise is statistically the same constantly”, we have very many honeypot trajectories, which for obvious reasons are an attack on the carrier that an adversary would normally use for analysis (flooding with data for analysis) in practice excluding the attack on the buffer itself, because one must first find the buffer in the continuous noise and in such a case the asymmetry of costs between the adversary and the user becomes absurd. ----------------------------------------------------------------------------- Conclusion about practical application 2: Instead of using a directly digital channel on a known band we can use n-bands and exactly the same, on the basis of _0 (current zero key K0) use skip. In the digital version “band” is one PACK: a homogeneous sequence {scale, msg, ERR}. The recipient with the current K0 does DECRYPT → CHECK → AO_check(S, ERR) and either assembles 480 bits and rotates the bunch, or skip. Fakes are in the same format; the marker of truth does not exist in the channel. K0 selects which band at this moment is the legal trajectory, and the others are honeypots with the same ERR statistics. We obtain a channel with controlled degradation (and not a cryptosystem), only immersed in a frequency multiplex (in n independent carriers, n OFDM subcarriers, n radio channels, n files, n sockets). This requires a longer description, because conceptually we will return to the 1990s of the last century of the previous millennium. I have: * K₀ - current key at position _0 in the HSM (after possible rotations), * n - number of bands, * t - index of the tick / time frame, * H₀(t) - HSM package from current_0 at moment t (Flist, Mbd, pool, N, noise). From K₀ and t (or from the already existing splitmix or hash from #4) one calculates the band selector, without FPU, purely integrally - just as QCOpool and Enoise: b*(t) = π_{K₀}(t) mod n This is analogous to DETERMINISTIC_SHUFFLE as a bijection or hash on band indices. It does not carry the mantissa tail. It is not a secret “which bit is the message”, only which automaton at this moment has the right to close the ERR loop. Nothing in principle follows from this. On each band b = 0, …, n − 1 a legal package of the same type as in #10 flies: ĉ_{b,t} = {scale, msg, ERR}_{b,t}. The difference is only in the modulator seed: * on band b*(t): HMS_CALL("current_0") - true block (or silence formatted like a true block, if in this tick there is no opentext), * on the others: HMS_CALL("fake_0") as in #8, with the sieve ERR_f ≠ ERR_0. The physical channel sees n simultaneous carriers with the same statistical envelope. There is no “quiet band” and “loud band”. There are n copies of the same kind of noise. I perform skip on the basis of K₀ The recipient does not scan the content in search of sense. He does PERFORM_TRANSCENDO: 1. Snapshot of the bunch (history_make) - the same seed as the sender. 2. In tick t he reconstructs b*(t) from the current _0 (the same integral function). 3. From band b*(t) he takes ĉ and calculates v̂ = DECRYPT_MSG_V3(ĉ, H₀), S = CHECK_DECIPH(v̂). 4. AO_check(S, ERR): output 1 → this was the legal trajectory: glue 480 bits, possibly EXTRACT_NUMS from _1 and rotate_keyring. output 0 → skip of this tick on the selected band (desynchronization, collision, broken tick) or - in the hard variant - skip of the whole tick, because the selector and ERR must agree at once. Bands b ≠ b*(t) the recipient may not demodulate at all (saving) or demodulate solely so that the timing and power consumption look as with a full scan (the same side-channel comment as with false blocks in #11). I do not even know if this is necessary, after all we assume that on the side channel it is anyway obvious that the recipient knows which band to read, so only one is unknown which. But if there were another airgap there (read all and send the results behind a one-way curtain) then perhaps this would be essential for some security concept. Skip is therefore the product of two sieves, both already present in the algorithm i.e.: genealogy sieve: only _0 closes ERR, address sieve: only the band calculated from the same _0. An adversary who hears all n bands receives n times more honeypots than in the single-band version. Local success on one band (“this candidate gave a legal distribution of buckets”) does not say whether this was b*, or an ERR collision on a foreign seed - and the sender does not let such collisions on decoys (E_f ≠ E_0). Synchronization and rotation of the bunch remains bound exclusively to the accepted tick on b*, exactly as in #9 / #11: * a decoy on a foreign band does not allow calling rotate_keyring, * a wrong skip (receiving a decoy as truth) permanently derails the rings (therefore we have the history make operation in case of restoring synchronization to the last, correct ring), * lack of two non-NaN from the _1 backtracking leaves the bunch alone. After the first accepted tick the session is again not a function of the seed alone: Ring_{n+1} = R(Ring_n, G(K₀^{(n)}, K₁^{(n)}, m_n), b*(t)). The band selector in the next tick already sits on the mutated _0. Knowledge of the initial K₀ and of the full sequence of accepted (band, block) is needed to go further. “I have the seed and n recordings” alone is not enough because one must guess which of the n paths in each tick were the truth, because a mistake spoils G and further spoils b*. The cost grows as nᵀ hypotheses of division in time, not 2⁵³ one-time. One does not need a new FPU modulator for this. The FPU remains where it was in CRYPT_MSG_V3 / sedenions / Flist. The band is only an index of the multiplex. layer what it does place in the algorithm study HSM / K₀ seed + rotation #4, #9 Flist, QCO, rot16 block degradation #1-#3, #6 ERR + AO_check block skip #7, #8, #11 hash/splitmix(K₀, t) mod n band skip the same mixer as N, pool, noise n carriers traffic covering the discussed conclusion; analogy of cover traffic from the introduction to the work On the radio we have n subbands, in each the same symbol {16 × double + scale + 3 counters}. On the cable we have n parallel streams. In a file it is n containers of identical format. In the network it is n destination / n ports / n rendezvous. “Known band” disappears as a public codebook. Public is only the fact that some of the n bands in tick t can close the loop. Which one, and that is already a function of history, whose FPU projection anyway does not carry in msg. Taking into account the conclusion about practical application 1 where continuous noise on one channel + skip after ERR + mutation of the bunch at “noise which is not senseless” then conclusion 2 (the one just discussed) makes that the observer sees n equivalent carriers, and the legal automaton sees one trajectory K₀ ↦ b*(t) ↦ ĉ_{b*,t} ↦ G ↦ K₀′. We then have such a Pecora-Carroll without analog phase. Two HSMs with the same bunch stick not by a common choice (not by a physical signal, although if one considered this from the side “in the past a duplication of the signal to both HSMs was performed” one could point to that “physical signal” but this is stretched), of which signal not to skip. Snap-to-grid of the FPU does not synchronize the slave with the drive, it only cancels the slave because there are two calls of the same map, on the same band calculated from the same seed. There is no reason at all for it to be precisely Bob transmitting to Alice with the same bunch. I will note however that if one and the other transmitted from the same bunch then the trajectories would immediately diverge anyway, so we will obtain two sets of keys - transmitting and receiving. The only case where one can use one bunch for transmitting and receiving is one in which there exists a function (as in a walkie-talkie) that now “I am speaking”, and the others are listening. For the adversary the unit of attack is frame × band × history of rotation, and since ERR is a honeypot, and not an oracle of identification of K0 then the Oracle “does this candidate close the loop on band b?” answers a cheerful “yes” for the whole fiber of histories which fall into the same bucket. But this is not cryptography; here a wrong choice of band = skip or garbage after Solomon, not “wrong plaintext” or something similar, simply noise. n does not replace the length of the message because observational security anyway sits in the chain of true ticks, not in the width of the multiplex. n multiplies the honeypots and the cost of division. The physical layer must not betray b* by a side channel (power, PAPR, occupancy). Hence the option “demodulate all bands, skip in silence” from #11. Transmitter and receiver are entangled by the seed. The channel is n projections at once. K0 selects which projection not to skip. The rest of the bands are honeypot trajectories. The readout is lossy and sufficient. There is no oracle of which band was “the proper one” from the envelope alone, as previously there is none because there cannot be one. ----------------------------------------------------------------------------- At the end it would be appropriate to explain “humanly” what is happening here (in the application of the algorithm). This synthetic arithmetic channel (and not a radio one in the sense of wave physics, although one can lay it on n bands as in conclusion 2) does not come from a public codebook or thermal noise but from the grid of residua of a specific toolchain (the same FPU, uops, libm, order). Whoever has the seed and the same automaton stays on the ring. Whoever has only {scale, msg, ERR} gets a bucket (a gargantuan sudoku to solve), and not the history. The FPU is commonly available today, everyone has one. Here one needs a certain kind of fpu and specific libm (i.e. two identical machines F on both sides of the channel), but this is not some special problem in prepared P2P communication. Analytics over ℝ loses the object, it does not only become more expensive. Hill-climb, gradient and Pearson on neighboring keys assume a family F_K and a slope C(K, K′). On the channel there is the fiber q⁻¹(b). This is not even complicating the analytical question, it is changing the question. They spoiled the simplifying tool and left only combinatorics, whose number of false-positive solutions they packed into non-discriminated buckets without combinatorial composition. The declaration “protection with a 53-bit key” does not map onto security. 52+1 is the width of the mantissa and the width of one slot. K₁ is not in the carrier, does not deform the current block and cannot be read from {scale, msg, ERR}. The rotation is determined by the common wrong backtracking, without a prior relation K₁–buffer. This is an interesting mechanism. Negative verification for the holder of the bunch is cheap: one backtracking and comparison of the triple of counters. A fake does not move the bunch. For someone without the seed a locally sensible block does not identify the trajectory, and a wrong acceptance permanently derails the rings. It seems to me that this is the most essential part binding the whole loop into an efficient mechanism solving the problem of distribution of a symmetric, de facto infinite in 480-bit intervals key. K1 does not deform the current channel, but selects the next deformation. Whoever found the initial K_0 opens at most the first true block. Without K1 and without correct filtering of the decoys there is no G, so there is no next K0. The trace of K1 exists locally, on both sides, and only there. In the channel there is a deliberate transfer of semantics from the word to the genealogy. And there is no new primitive. Shor falls away categorically. There is no group order, period or hidden subgroup here on which Shor works. “Resistance to Shor” would be the same taxonomic error as “53-bit security”: the algorithm does not get its instance, so there is nothing to accelerate. Grover does not fall away (apparently). Its object exists: unstructured search for a seed for which AO_check passes on the proper subsequence and the bunch remains synchronized. Information destroyed in the radiator does not return, but the predicate “this seed holds the channel” is checkable, so the quadratic acceleration on this space remains. Grover does not remove the Kendall multiplier of the message length (injected fakes), the cost of the trajectory or the cost of a wrong division. Which makes it toothless on the whole because on 53 bits it opens at most a bag of blocks, and the session remains a separate puzzle. The length of the message and the slider of decoys multiply precisely this puzzle. Fake blocks are interesting because the adversary receives a homogeneous sequence {scale, msg, ERR}. There is no “fake” bit, no list of excluded keys, no trace that the sender calculated a collision at all. The sieve is local at the sender. It does not let in a sample because of reproduction of the fake report in K0. Without the bunch every entry is therefore an equally legal package. The relation is not transmitted. Exclusion of the collision is not information available to the adversary. ERR still cuts something publicly, only very weakly. A candidate who on this msg does not reproduce the attached triple drops out. There are few triples, many consistent seeds. This is not identification of a family. The machine is common, and its residuum is deterministic. The creators stop treating it as an error with respect to ℝ and set the cascade so that the history of roundings dominates over the word. The analyst is left with a fiber, not with a slope. BER is calibrated under ECC, not under bit equality (interesting idea). The decoy and the rotation through the non-transmitted K₁ make that even hitting the current K₀ is not a session secret. The channel gives deniability and separation of histories. It does not give a signature, identification or a hard problem. Against Shor there is no instance. Against Grover there remains search for the seed, only on a predicate which composes badly. I also found discrepancies of the implementation with the plan contained in the working notes. In the notes there is a scalar functional on the hierarchy of blocks. Local norm N_k = ∑_{x ∈ B_k} x², weight w_k = 2^{−2^k}, not 2^{−k}. The object does not agree! So I checked (I interrogated) why? Object: Q(x) = ∑_k 2^{−2^k} N_k was tested in Python on a word of length 1024 bits. Then they examined that the vanishing of the gradient is faster than exponential and the horizon: the contribution of a deep block disappears from Q before any algorithm has time to distinguish it. In a 52+1 mantissa this leads to an immediate generator of white noise. The exponent is too severe and nothing can be reconstructed from this (the channel is completely destroyed and even approximate reconstruction is not possible). They therefore had to invent how to fit this under BER and therefore the third version (CRYPT_MSG_V3 — as can be seen v3) is calibrated by other operations (and the name QCO-like remained): It embeds 16 30-bit words on the middle of the bucket. Then QCO_LIKE does not sum weighted norms. It raises the coordinates from the permutation: π[2], π[3] to the square, π[4..7] to the fourth, π[8..15] to the eighth. π[0] and π[1] it leaves. This is four manually manned floors on 16 axes, not a series 2^{−2^k}. Further an explicit scale and N ∈ {52, …, 68} left multiplications of sedenions by the angle 2π · ŷ_r, where ŷ_r comes from the quotient of prime roots, not from Q(x). The output is sixteen doubles, the scale and ERR. Not the scalar Q. The theorem about I(B_k; Q) → 0 does not transfer to this package by itself. But... In the code “QCO” is a finite, malicious truncation of the tail before multiplication. In the notes it is the informational boundary of the functional. In the notes the weight is a knob, not a constant. Goal A, stability, is w_k ~ 2^{−k}. Goal C, maximal irreversibility, is precisely w_k = 2^{−2^k}. With the hierarchy from the notes block k = 10 closes 1024 components and carries the weight 2^{−1024}. In binary64 ulp(1) = 2^{−52}. The contribution of a block added to a sum of order 1 disappears when the weight falls below this threshold: k weight in a sum of order 1 4 2^{−16} still visible 5 2^{−32} still visible 6 2^{−64} already zero 10 2^{−1024} zero; the number itself is subnormal, it is not in the accumulator The gradient ∂Q/∂x = 2 w_k x for k ≥ 6 is identically zero in double. The deep block is not difficult, simply nothing remains in Q. If the carrier was supposed to be this functional or these deep components: BER about 50%, not 2%. ECC then has nothing to correct, the channel is closed, not noisy. The number 21/1024 from the implementation notes is already a measurement of the calibrated channel, about 2% on the proper key. Not the result of the raw Q. QCO_LIKE does not multiply the norm of the block by 2^{−2^k} according to the original plan (because the test was done for a gigantic mantissa). It raises the coordinates already embedded in (0, 1]: floor 2 to the square, floor 4 to the fourth, floor 8 to the eighth. This is a relative of the hierarchy without truncation of the weight. The power itself does not zero the axes. For x = 1/2 the eighth power is 2^{−8}. For x = 0.1 it is 2^{−26.6}. Both sit above the subnormal. Ulp at one does not destroy them. Therefore the very name “QCO-like” is an honest kinship, but not equivalence; it is an engineering application within the constraints. And immediately it has the calibration written out in the code. Common with the original object is that the higher floor is to disappear first. A different tool was used, because in the notes the coefficient is below the addition threshold, and in the implementation a shift of the axis into a coarser part of the grid, and only then several dozen projections Π. The division of roles is the same. Something has to fall out of resolution before any algorithm distinguishes it. Something has to remain, otherwise there is no message. In double after the raw Q only a few first blocks remain, k ≤ 5. The rest of the 1024-bit word is the horizon. No BER calibration is extracted from this, because the vanishing is a step to zero, not a level of 2%. V3 spreads this step over the width of the mantissa that it actually has, i.e. the carrier is 30 bits out of 53, the rest of the register is the budget for the tail, the powers 2/4/8 slide the higher axes by a few to several dozen bits, not by 1024, N ≈ 52–68 sedenion multiplications pull the rest by iterated rounding; Lyapunov (measure) is thrown out around round 30–32, further it is pouring into the full bucket, the proper trajectory leaves about 2–2.5% bits for ECC, the neighboring about 55%. They engineered the same division of work with a milder exponent. Killed is the tail and the genealogy, the equivalent of the deep block. Alive is the 30-bit symbol, the equivalent of the shallow k whose weight still entered the sum. The whole calibration stands on the fact that the goal destroys the channel, and the channel was supposed to remain (for communication). The working notes (the difficult ones from the sources) tell one to set the tempo of loss with the weight, not to treat 2^{−2^k} as obligatory. The third version is the choice of a milder tempo, written in operations which the FPU performs anyway, instead of a coefficient which the accumulator would zero. That is, I found traces of engineering at the calculations. Ordinary numerical calculation reduces the error to zero: longer mantissa, renormalization, compensation. An ordinary cipher demands bit equality, D_K(E_K(M)) = M. An ordinary channel does the opposite. It measures BER and chooses the code so that the correction radius covers this BER with a margin. Reed–Solomon (255, 223) has 32 redundant symbols and corrects up to 16 symbols per block, practically a few percent of byte errors. LDPC and polar codes take even higher, provided the errors are not maliciously clustered. Here the channel is designed under this threshold, not measured after the fact. The proper seed is to remain at about 2–2.5%, i.e. inside the code. The neighboring one, from the memory of the test tool in the code, lands around 55%, i.e. outside every practical ECC. The specification is the distance to the codeword smaller than t, not bit equality. The calibration knobs are visible in the loop. The number of rounds matched to the mantissa, cutting the band to 30 bits, embedding in the middle of the bucket instead of on the dyadic edge, the powers 2/4/8 (QCO-like) eating the higher axes, lack of any contraction back to ℝ. ECC does not sit in the operator. It sits above it, and therefore one can put a ready ciphertext inside. The channel has no relation to what it carries. The object which the analyst needs is spoiled. Instead of a slope there is a fiber. Solutions multiply in the same bucket. Packages indistinguishable in format are added. Rotation through the non-transmitted K1 makes that hitting the current K0 is not a session secret. They repeat this over the length of the message, so a wrong division does not compose from good divisions. There remains iteration of seeds times the puzzle of subsequences. There does not remain the question “which key inverts the function”, because the observation of this function does not determine it. The predicate exists. It is only bad as an object of attack, for which gradient, and Shor, and the mere opening of the block are prepared. ============================end of the work============== Additional materials # with detailed discussions. ================================================================================================ #1_return_base_fraction.txt ================================================================================================ #Fragment of the code return_base_fraction, the whole has a separate external document 1_return_base_fraction.txt Fragment of code with algebraic expansion for R, //you can put this as a footnote beside, on the final pages as a reference because it is long! That is, cut all of this out here and it will be an “additional booklet” concerning one tiny loop of code expressed below. Here you will insert a reference that somewhere at the end this is written out; you can insert the pseudocode and its explanation here, and move the description of the divergence to the end for longer explanations. pseudocode of the input value (so as not to reveal the key to the machine) from the hardware security module emulator, then transferred to the operation: INPUT: k > 0, r ∈ ℕ (even), sequence of primes p[0], p[1], … (p[0] = 3) OUTPUT: f = L / R L ← k R ← k^{1/2} // two already used m ← r / 2 for j ← 0 to m-1: L ← L + k^{1 / p[2j]} R ← R + k^{1 / p[2j+1]} f ← L / R return f //================ here this fragment of code ends, and its explanation begins. Explanation of the code: _left_side starts from k¹, _right_side from k^{1/2}; the loop i += 2 divides the successive primes into two sets of indices: even ones to the numerator, odd ones to the denominator; power(_key, 1./prime_list[…]) is exactly k^{1/pᵢ} in ℝ; the result is the quotient of two finite sums of powers with exponents the reciprocals of primes. Interpretation in ℝ Let k > 0 (argument _key) and let (pᵢ)_{i=0}^∞ be the sequence of prime numbers starting from 3: p = (3, 5, 7, 11, 13, 17, 19, …). The square root is already separated separately as k^{1/2} (i.e. the two has been “used up”). For a fixed number of rounds r = _rounds (even, so that the loop is well-defined) we define two finite sums: L(k, r) = k + ∑_{j=0}^{m-1} k^{1/p_{2j}}, R(k, r) = k^{1/2} + ∑_{j=0}^{m-1} k^{1/p_{2j+1}}, where m = r/2. The sought value is the quotient in ℝ: f(k, r) = L(k, r)/R(k, r). Each term k^{1/p} is well-defined for k > 0 as exp((1/p) log k). The operations + and / are ordinary addition and division in the field ℝ. Divergence! In R we obtain analytical convergence to 1 independently of the key k > 0. For any fixed k > 0: k^{1/p} →_{p→∞} 1, because k^{1/p} = exp((ln k)/p) and (ln k)/p → 0. Precisely: k^{1/p} − 1 ~ (ln k)/p. After m turns of the loop (i.e. m pairs of additions) I have L_m = m + k + ∑_{j=0}^{m-1} (k^{1/q_j} − 1), R_m = m + k^{1/2} + ∑_{j=0}^{m-1} (k^{1/s_j} − 1), where q_j are the primes with even indices (3, 7, 13, …), and s_j with odd ones (5, 11, 17, …). The sum ∑ 1/p over primes diverges like log log X, so the corrections A_m = ∑(k^{1/q_j} − 1) grow (or decrease, when k < 1) slowly. Whereas the number of terms m grows like half of π(X), i.e. like X/(2 log X). Hence A_m = o(m) and B_m = o(m), i.e. f_m = L_m / R_m = (m + A_m)/(m + B_m) →_{m→∞} 1. For k = 1 the equality f_m = 1 holds already for every finite m. The limit does not depend on k; only the tempo depends on k, so the farther k is from 1 (large |ln k|), the more rounds are needed for the terms k^{1/p} to manage to fall close to 1. Meanwhile in the fpu the mantissa really dies. Real analysis loses its subject, and what remains in the result is no longer what this analysis describes. I have two different objects In ℝ after m pairs: f_m(k) = L_m(k)/R_m(k) = (m + A_m(k))/(m + B_m(k)) → 1, independently of k > 0. The remainder A_m, B_m is o(m), so ℝ says: the key disappears, the one remains. On the machine there are no L_m, R_m ∈ ℝ. There are two accumulators and rounded operations x ⊕ y = fl(x + y), x^{⊕ 1/p} = fl(x^{1/p}), u/v ↦ fl(u/v). The implemented function is f̂_m: 𝔽 × ℕ → 𝔽, not f_m: ℝ → ℝ. The equality f̂_m(k) = f_m(k) is not part of the definition — it is an accidental approximation. This feature sits inside the construction of the quotient: L ← fl(k) R ← fl(√k) for j = 0..m-1: L ← L ⊕ fl( k^{1/q_j} ) // rounding of the term and of the sum R ← R ⊕ fl( k^{1/s_j} ) f̂ ← fl(L / R) // quotient of already deformed registers Three layers which ℝ does not have: Term. k^{1/p} almost never lies in 𝔽. The machine puts into the register not the power, but the nearest (or rounded toward even) element of the dyadic grid. Sum. ⊕ is not associative. L and R receive disjoint sequences of primes, so they collect disjoint sequences of errors. Numerator and denominator are not two ends of the same real number — it is a pair (L, R) ∈ 𝔽². Quotient. The result is fl(L/R), not fl(L_ℝ / R_ℝ). You round the quotient of two already deformed quantities. This is precisely floating-point quotient arithmetic: the subject is the ratio of registers, not a value from ℝ after rounding. Therefore the residuum f̂_m − f_m on double is of the order of ulp — and this does not refute the thesis. The carrier of what the function does with the key becomes the layer which the analysis in ℝ treats as vanishing. The feature (not defect) consists in the fact that in ℝ the larger m, the more k is invisible: information about the key escapes into A_m/m. On 𝔽 the opposite migration occurs: - the algebraic part (the one which ℝ describes well) is pushed toward 1; - the part which distinguishes keys remains in the trajectory of roundings: which terms fell above/below the middle of the ulp, in which binade the accumulator was, whether fl(L/R) received the bit upward or downward. What in ℝ is o(1) and “to be neglected”, in 𝔽 is the entire remaining content of the result, when f_m already sits close to 1. Real analysis becomes ineffective not because the series diverges, but because it predicts the vanishing of what the machine preserves. ℝ 𝔽 m → ∞ f_m moves forever, f_m = 1 + O((ln k log log X)/m) from a certain m the terms are 1.0, then L ⊕ 1 = L; the orbit is a constant state or a ulp jump dependence on k vanishes in the limit does not vanish: another key is another trajectory of roundings and another pair (L, R) quotient one number quotient of two independently rounded registers Summary of the pseudocode and the feature of the automaton. Let fl be the projection onto the chosen 𝔽 (e.g. binary64). The procedure realizes f̂_m(k) = fl( (fl(k) ⊕ ⨁_{j 1_return_base_fraction.txt where there is such an algorithm; Conclusion — rotation is very good in order to “derail” the representation R|float, but double holds variables so well, and the fpu has such a reserve of resolution, that in normal applications (3d graphics) the matter of “error” (the feature which we want to exploit) is negligibly small and functions as a glitch which is mitigated. I need an operation which will bring this to absurdity instantaneously. It will be the operation of rotation of a 16D vector in the complex plane by an angle which definitely exceeds the boundary of the representation F. All operations will be performed textbook-wise (with explanations why algorithmic peculiarities and complicating the algorithm is pointless, because analyzable). ================================================================================================ ================================================================================================ #3_generator_mapy.txt ================================================================================================ Pseudocode and discussion. Creation of the arithmetic map using the previously indicated arithmetic pathologies of the machine. assumptions: base ∈ ℝ is already a double representation with full mantissa rounds ∈ {52, …, 68} P[n] - the n-th prime number key - secret/parametric input fract_list ← ∅ for r = 0, 1, …, rounds-1: x ← base // start EVERY TIME from the same seed x ← EXE_RETURN_FRACTION(key, r, x, rounds) fract_list["_" + str(r)] ← x function EXE_RETURN_FRACTION(key, r, x, rounds): h ← HASH(str(key) ∥ str(r)) // sha1 / splitmix / any mixer a ← CODEPOINT(h, 1) b ← CODEPOINT(h, 17) c ← CODEPOINT(h, 13) small ← (((a ⊕ b) mod 211) − 101) / 1009 // ≈ (−0.1001, +0.1080) tiny ← (((c ⊕ b) mod 53) − 29) / 1279 // ≈ (−0.0227, +0.0180) typ ← ⌊(c ⊕ a) + rounds⌋ mod 4 // {0,1,2,3} if typ = 0: x ← x + (r + √P[r+3]) · (√P[r+2] + small·√P[r+1] − √P[r]) x ← √(rounds + small + tiny) + x^(1/P[r]) x ← x · (2·(r mod 2) − 1) // sign (−1)^r if typ = 1: A ← (x + small)^(1/P[r+11]) B ← (x + tiny )^(1/P[r+9]) x ← arcsin(A / B) if typ = 2: x ← (x − tiny)^( 1 / (P[r+8] − small) ) if typ = 3: x ← x^( 1 / (P[r+10] − tiny) ) return x ============================================== Important: this is not an iteration xᵣ₊₁ = Fᵣ(xᵣ). This is a family of independent branches x⁽ʳ⁾ = Fᵣ(base). Each degeneration sits inside a single Fᵣ, not in composition over r. What is algebraically irreversible already in ℝ Even if one calculated in exact real arithmetic, the maps are not injective. High roots. For p ≥ 2 and x > 0: x ↦ x^(1/p) is strictly increasing on (0, ∞), so invertible there. But: on x < 0 it does not exist in ℝ when 1/p is not a fraction with odd denominator (for most primes p — it does not exist), x = 0 is a fixed point and “glue”: the whole neighborhood 0⁺ goes into 0⁺, x ↦ x^(1/p) flattens (0, 1) to a neighborhood of 1 when p grows: lim_{p→∞} x^(1/p) = 1 for every x > 0. With P[r] ∼ r log r and r ≤ 67 this is not yet the limit, but already a strong contraction around 1. Composition “sum + root” (type 0). First you add a quantity of order Δᵣ := √(Pᵣ₊₂) − √(Pᵣ) + small · √(Pᵣ₊₁), then you take x^(1/Pᵣ). Adding a constant (dependent on r, not on x) is invertible; the root — only on x ≥ 0. The sign at the end is invertible. The whole is invertible on the half-line on which after the first step the argument of the power remains ≥ 0. Quotient of two roots + arcsin (type 1). x ↦ arcsin( (x + small)^(1/p) / (x + tiny)^(1/q) ) is defined in ℝ only when x + small ≥ 0, x + tiny ≥ 0, |(x + small)^(1/p) / (x + tiny)^(1/q)| ≤ 1. arcsin is injective on [−1, 1], but the quotient of two different roots — is not. Different x give the same ratio, and the domain is already cut out. Types 2 and 3 are again x ↦ x^(1/α) with α ≈ P_• ∈ [2, 400]. Invertible exclusively on [0, ∞) (possibly on ℝ when the exponent has the form 1/(2k+1)). Where the FPU makes pathologies out of this (this is the core) Double has 52 bits of mantissa + the hidden bit. Your base has already used these bits, so every further operation which is not multiplication by a power of two destroys information irreversibly. Below are concrete mechanisms, dependent on the input. 1. Catastrophic subtraction in type 0 √(Pᵣ₊₂) − √(Pᵣ) = (Pᵣ₊₂ − Pᵣ) / (√(Pᵣ₊₂) + √(Pᵣ)). The gap between successive primes is O(log r) (on average), so the difference of roots is O(log r / √(r log r)), for r ∼ 50…70 of order 10⁻¹. small · √P[r+1] is of the same order as the difference itself. In double: * both √P have ulp about 2⁻⁴⁸ in the neighborhood of 15…20, * after subtraction a few significant bits remain, * multiplication by r + √(Pᵣ₊₃) ≈ 60…90 only shifts this already noisy difference. The contribution of base (full 52 bits in the fraction tail) is added to a quantity whose rounding errors are already comparable to ulp(Δᵣ) · r. If |base| ≪ |Δᵣ · r|, the tail of base dies in the noise; if it is comparable, mixing occurs, but the map ceases to be invertible, because addition in FP has no inverse: fl(x + y) − y ≠ x when |y|/|x| exceeds 2⁵², and here it often exceeds partially. 2. Flattening by x^(1/p) — exponential loss of bits in p In logarithmic scale: log₂(x^(1/p)) = (1/p) log₂ x. Distance from 1: x^(1/p) − 1 ≈ (ln x)/p. If after the first step of type 0 you get x = O(1) or x = O(10), then (ln x)/Pᵣ falls to 10⁻²…10⁻¹. The mantissa of the result occupies bits near 1, where ulp(1) = 2⁻⁵². Information from the distant tail of base is compressed by a factor ≈ 1/p and then rounded. This is the classical irreversible contraction to 1. For type 3 directly: x ↦ x^(1/(Pᵣ₊₁₀ − tiny)). For type 2 the exponent is 1/(Pᵣ₊₈ − tiny); here an additional pathology: P − small is not integer, so this is not an algebraic root, only exp(ln(x − tiny)/α). In FP: * ln of an argument near 0 explodes, * ln of a negative argument → NaN, * exp of a very negative → underflow to 0, * exp of a large → Inf. 3. Type 1: two sources of NaN / domain + quotient ≈ 1 Let p = Pᵣ₊₁₁, q = Pᵣ₊₉, both large. * If base + small < 0 or base + tiny < 0 → pow in ℝ does not exist; in FPU usually NaN (in GM/IEEE pow of a negative base and non-integer exponent). * If both arguments ∈ (0, 1), both roots sit in (1 − ε, 1), the quotient in (1 − δ, 1 + δ). arcsin is then almost constant near π/2 or almost linear near 1, depending on the sign of (A/B − 1). * If B → 0⁺ (when base ≈ −tiny), the quotient → ±∞ → arcsin outside the domain → NaN. * If A and B are both near 1, subtraction A − B in the quotient A/B = 1 + (A − B)/B is again cancellation. This is the most “explosive” type with respect to the input: a small change of base by 10⁻¹⁶ can throw you across 0, across 1, or across the edge of the arcsin domain. 4. Sign, zero and parity of r (type 0) The final x ← x · (2(r mod 2) − 1) is invertible. But earlier x^(1/P[r]): * if after adding Δ the argument is negative, FPU gives NaN, * if exactly 0 (possible after cancellation), you get 0, then √(rounds + ⋯) + 0, then the sign. Zero is absorbing. 5. Denormals / underflow / overflow * tiny, small are O(10⁻²). If base is O(10⁻²) and type 2 does base − tiny ≈ 0, you get denormal or 0, then a power of the denormal. * Type 0 can push |x| to tens–hundreds before you take the root; this is safe (no overflow), but loses the fraction of base (point 1). * arcsin is limited to [−π/2, π/2], so type 1 always compresses the range — another irreversible loss of scale. 6. Hash → small/tiny/typ as discontinuous switches a, b, c are codepoints of hex SHA1, so discrete. typ jumps over {0, 1, 2, 3} with a change of 1 bit of the key or r. This is not a pathology of the FPU, only discontinuity of the system: neighboring keys fly into different families of maps (additive + root vs arcsin vs pure root). Algebraically there is no single analytic branch over key. small and tiny are piecewise-constant (image of a finite set of codepoints). The formula small = ((a ⊕ b mod 211) − 101) / 1009 is not “uniform random noise” — it is a lattice with step 1/1009 ≈ 9.9 · 10⁻⁴. In double this step is enormous relative to ulp(base) if |base| ∼ 1. The perturbation does not mix the tail of base bit-by-bit; it shifts the argument by a coarse grid, and the tail is destroyed only by the transcendental operations. Pathological behaviors dependent on the input — map of cases Let x₀ = base. Essential thresholds: Condition on x₀ Type (from the code equivalent of switch) What happens in double x₀ + small < 0 or x₀ + tiny < 0 1, 2 NaN from pow x₀ ≈ 0 2, 3 underflow / 0 / denormal, then sticking to 0 or 1 x₀ ≈ −tiny 2 argument of the power ≈ 0, explosion of ln or 0 x₀ > 0 large (≫ 1) 0, 3 tail of x₀ dies in addition; root pulls to 1⁺ 0 < x₀ < 1 1, 3 strong contraction to 1, then arcsin ≈ arcsin(1) = π/2 when both roots ~1 |A/B| > 1 1 NaN from arcsin cancellation √(Pᵣ₊₂) − √(Pᵣ) ≈ −small √(Pᵣ₊₁) 0 Δᵣ ≈ 0, then the tail of base survives; otherwise the tail dies Pᵣ₊₈ − small ≤ 0 2 negative / zero exponent: pow → Inf / NaN / division by 0 in ln/α Pᵣ₊₁₀ − tiny ≤ 0 3 analogously (in these ranges tiny is almost impossible, because P ≥ 2, tiny > −0.03) The only realistically “safe” positive exponent is type 3: Pᵣ₊₁₀ − tiny > 1 always in this range. And “by chance” this is always the case for any key in the range of integers in the fpu limit. If I wanted invertibility in ℝ or at least “almost invertibility” in FP This system is not a diffeomorphism even piecewise: 1. Four branches do not glue smoothly (discrete type). 2. pow and arcsin cut out domains. 3. fl(x + y) and fl(x^(1/p)) project whole intervals onto the same double (many x₀ with full mantissa land in the same result after 1/p). 4. Type 0 has built-in cancellation — the number of significant bits of the result depends on the case, not on base. 5. Starting from the same base for every r means that the list _fract_list is not an orbit of dynamics, only sampling of four families of maps at points indexed by the hash. Irreversibility is per-branch. If the goal is “a pathological tail which cannot be reversed through the FPU”: * type 0 gives loss of bits through cancellation + non-associative addition, * type 1 gives NaNs and flattening to {NaN, ±π/2}, * types 2/3 give contraction to 1 or sticking in 0, plus NaN on negative bases. These are exactly those irreversibilities which the FPU cannot reconstruct: there does not exist a function G realizable in double such that G(Fᵣ(x₀)) = x₀ for a full measure set of full-mantissa x₀. Analysis with expansion, i.e. how many bits the FPU ate. I remind that our goal is to obtain deformed, non-monotonic arithmetic, not complete chaos of data loss. Scale of the FPU which I use ε = 2⁻⁵², ulp(x) = 2ᵉ⁻⁵² for x ∈ [2ᵉ, 2ᵉ⁺¹), ulp(1) = 2⁻⁵². sqrt, +, * are correctly rounded in IEEE (error ≤ ½ ulp). pow / exp∘ln is not correctly rounded and does not have to be monotonic. This is the source of that small, unstable divergence, not “smooth contraction to 1”. Indexing: P[0] = 2, P[1] = 3, … (as in the table). For r ∈ {0, …, 67}: P[r] ∈ [2, 337], √(P[r]) ∈ [1.41, 18.36], Δ₀ := √(P[r+2]) − √(P[r]) ∈ [0.24, 1.08]. Type 0 — three stages and Taylor I denote: sₖ = √(P[r+k]), M = r + s₃, Δ = Δ₀ + small · s₁ = (s₂ − s₀) + small · s₁, A = M Δ, z = x₀ + A, ρ = √(R + small + tiny), y = σ(ρ + z^(1/P[r])), σ = 2(r mod 2) − 1. From the numbers: M ∈ [2.6, 86], A = M Δ typically O(1) to O(50). For r ≳ 20 the term small · s₁ dominates Δ₀: the ratio |small| s₁ / Δ₀ grows from ∼ 0.2 (r = 0) to 3…6 (r = 50…67). The skeleton of primes yields to the hash grid. The hash has only log₂ 211 ≈ 7.7 bits in small = (k − 101)/1009, k ∈ {0, …, 210}. The rest of the bits of A come from the irrationality of √(P[•]) and from roundings. Stage I — cancellation in Δ₀ Δ₀ = (P[r+2] − P[r]) / (s₂ + s₀). In double you subtract two roots from the neighborhood of 2ᵉ ∼ 2³…2⁴, so ulp(sᵢ) = 2⁻⁴⁹ or 2⁻⁴⁸. The number of bits which survive in Δ₀: b_{Δ₀} = log₂(Δ₀ / ulp(s₂)) ≈ | 50.7 | r = 0 | 49.7 | r = 10 | 48.2 | r = 20 | 47.1 | r = 50 | 46.4 | r = 67. 53 − b_{Δ₀} ≈ 2…7 bits die. This is still mild. The dirty tail of Δ₀ anyway drowns in small · s₁, which is O(1) and full-mantissa (because small · √p with small on the grid 1/1009). Multiplication by M is almost lossless: M has a small integer numerator plus one root, the result A lands in [2, 64) and has a full 53 bits, of which the lower 2…7 are already noise of cancellation + rounding. Stage II — adding x₀ (seed tail) z = fl(x₀ + A) = x₀ + A + δ₊, |δ₊| ≤ ½ ulp(z). If x₀ is a fraction with full mantissa and |x₀| ∼ 1, and A ∼ 2ᵉ (e = 1…6): * bits of x₀ finer than ulp(A) fall off immediately, * alignment shift: e − e_{x₀} bits. For A ∈ [8, 32], ulp(A) = 2⁻⁴⁹…2⁻⁴⁷. Of the 53 bits of x₀ there remain after addition b_add = 53 − e ≈ | 51…52 | |A| ∼ 2 (small r) | 49…50 | |A| ∼ 16 | 47…48 | |A| ∼ 50 (r ∼ 60). This is still a lot. The tail of x₀ does not die here, it only gets pushed into the lower part of the mantissa of z. The irreversibility of addition is weak: fl(z − A) reconstructs x₀ with error O(ulp(z)), i.e. you lose those 2…6 bits of alignment, not the whole mantissa. Here the first non-monotonicity appears: if x₀ + A sits at a binade boundary, fl(x₀ + A) can jump by 1 ulp “in the wrong direction” relative to the ordering by x₀. This is still rare, because add is correctly rounded and monotonic in every fixed binade. The jump is only on the boundary of 2ᵉ. Stage III — z^(1/p), here sits the unstable grain Let p = P[r]. Exactly z^(1/p) = exp((1/p) ln z) = 1 + (ln z)/p + (ln z)²/(2 p²) + (ln z)³/(6 p³) + ⋯ For your range z ∈ [2, 90], ln z ∈ [0.7, 4.5]: (ln z)/p ∈ | O(10⁻¹) | p = 2…11 | O(10⁻²) | p ∼ 30…80 | O(10⁻³) | p ∼ 200…337. The Taylor remainder O((ln z)² / p²) falls like 1/p²: for r = 0 it is O(10⁻¹), for r = 52 already O(10⁻⁵). The trend is therefore z^(1/p) = 1 + (ln z)/p + O(p⁻²), i.e. drift to 1⁺, but this is only the skeleton. What interests us is the differential: d/dz z^(1/p) = (1/p) z^(1/p − 1) ≈ 1/(p z) (z^(1/p) ≈ 1). A change of z by 1 ulp(z) moves the result by δy ≈ ulp(z) / (p z). In units of ulp(1) = 2⁻⁵²: δy / ulp(1) = ulp(z) / (p z) · 2⁵² = 2^{e_z} / (p z) ∈ (1/(2p), 1/p). That is, one ulp of input gives 1/(2p)…1/p ulp of output. To move the rounded z^(1/p) by 1 ulp one needs N_ulp(z) ∈ [p, 2p] ulps of z. The lower log₂ p bits of the mantissa of z are below the result grid in the first-order model. For successive r: r p log₂ p bits of z below the grid of z^(1/p) 0 2 1.0 ∼ 1 5 13 3.7 ∼ 4 10 31 4.95 ∼ 5 20 73 6.2 ∼ 6 52 241 7.9 ∼ 8 67 337 8.4 ∼ 8 This is the seed of the trend, not yet the FPU. In exact ℝ the map z ↦ z^(1/p) is strictly increasing. In libm there is z^(1/p)̂ = fl(exp(fl(ln z) / fl(p))) or exp2(log2(z)/p). Each fl throws onto the grid. When the true increment is a fraction of an ulp — and from p ≳ 20 it is — the result jumps: * 0 ulp (plateau: many successive z give bit-wise the same), * +1 ulp (in accordance with the trend), * −1 ulp or a double jump, when ln and exp fall on opposite sides of the midpoint. This is that small divergence which is not stable: the sign and moment of the jump depend on which side of n + ½ ulp ln z sits and whether p in the denominator is represented exactly (primes > 2⁵³ are not — but your p ≤ 337 are exact). The trend 1 + ln z / p is visible only after averaging many r or after a coarse shift of z. Locally, on the scale of the tail of x₀, the course is stepped and locally non-monotonic. Stage IV — adding ρ ≈ √R and the sign ρ = √(R + small + tiny) ∈ [√(52 − 0.13), √(68 + 0.13)] ≈ [7.20, 8.26]. ρ is a correctly rounded root, full 53 bits, neighborhood of 2³. The sum ρ + z^(1/p) ∈ [8.2, 9.4] (for large p; for p = 2 it is sometimes 9…12). You enter the binade [8, 16), ulp = 2⁻⁴⁹ = 8 ulp(1). The jitter from z^(1/p) had a grid of 2⁻⁵². After adding ρ the grid coarsens 8×: another log₂ 8 = 3 bits of this jitter die. The sign σ = ±1 destroys nothing. How many bits of x₀ really tick the result of type 0 Compose II+III+IV. Let |x₀| ∼ 1, z ≈ A. A bit of x₀ of weight 2⁻ᵏ moves z^(1/p) by 2⁻ᵏ / (p z), and after adding ρ you compare this with ½ ulp(ρ + 1) ≈ 2⁻⁵⁰: 2⁻ᵏ ≳ 2⁻⁵⁰ · p · z ⇒ k ≲ 50 − log₂(p z). Concretely, the last bit of x₀ which can flip the final double: r pz ≈ k_max surviving bits of the tail of x₀ ∼ 1 0 ∼ 6 ≈ 47.4 ∼ 47 10 ∼ 5 · 10² ≈ 41 ∼ 41 20 ∼ 2 · 10³ ≈ 39 ∼ 39 52 ∼ 1 · 10⁴ ≈ 36.7 ∼ 37 67 ∼ 3 · 10⁴ ≈ 35.1 ∼ 35 The lower 53 − k_max ≈ 6…18 bits of the seed are under the threshold of the final ulp in the linearization. In fact, through the steps of pow, even bits above k_max do not give a monotonic increment: they give a plateau or a jump of 1 ulp in an unpredictable direction, depending on the phase of ln z relative to the grid. If |x₀| ≪ 1, set k ← k + log₂(1/|x₀|): the tail even shorter. If |x₀| ≫ A, the alignment reverses and it is A that drowns in x₀ — then almost the whole mantissa of x₀ survives, and the primes + hash remain noise in the tail; pow will still compress the relative information by the factor 1/p. What type 0 does “pathologically” with the arithmetic: 1. A(r, hash) sets a coarse rail (scale 1…50) onto which x₀ sits as a perturbation. 2. Algebraically z ↦ z^(1/p) is a smooth relative contraction by 1/p. 3. In double this contraction does not reach 1 smoothly: it lands on steps of width p…2p ulps of z. 4. Adding ρ ∼ 8 eats another 3 bits of these steps. 5. The result therefore has * a coarse, reversible body: sign, ρ, 1 + ln A / p, * and an unstable tail of 1 ulp, which remembers only the upper ∼ 35…47 bits of x₀ and that non-isotonically. This is exactly the regime “there is a trend, the seed is too coarse, what interests us is the grain”. For p = 2, 3, 5 the grain is still dense (steps every 2…10 ulps of z). For p ≥ 73 the steps are already wide on 6…8 bits of the mantissa of z. The above generator is the key core appearing in the maps. The others are a minor nuisance adding discreteness to the course (key-dependent trajectory). Type 3 — pure root, the same mechanism without the rail y = x₀^(1/α), α = P[r+10] − tiny ∈ [29.98, 397.03]. Taylor the same, only z = x₀, without A and without ρ: y = 1 + (ln x₀)/α + O(α⁻²) (x₀ > 0). Output grid: N_ulp(x₀) ∈ [α, 2α]. The last bit of x₀ ∼ 1 which ticks y ∈ [1, 2): k ≲ 52 − log₂ α ≈ | 47 | α ∼ 31 | 45 | α ∼ 127 | 44 | α ∼ 241 | 43 | α ∼ 397. Without adding ρ there is no this loss of 3 bits — the jitter remains in the binade [1, 2) and is finer than in type 0. On the other hand there is no rail A: the whole y − 1 is already only ln x₀ / α, so for large α the body of the result sits in O(10⁻³) and almost the whole mantissa of y is steps of pow. Negative x₀ → NaN (for an odd denominator in ℝ there would be a sign, in pow of libm usually NaN). Type 2 — like 3, plus shift to zero y = (x₀ − tiny)^(1/β), β = P[r+8] − small. small ∈ (−0.100, 0.108), so β avoids the integrality of primes by O(10⁻¹). This is always exp(ln(u)/β), never an algebraic root. The new pathology is at u = x₀ − tiny → 0⁺: ln u → −∞, d/dx₀ y = y / (β u). Sensitivity explodes like 1/u. In double: denormals, then flush-to-zero, then ln → −∞, exp → 0. Several neighboring x₀ (even distant by many ulps on the scale of 1) stick to +0. On the other side of zero: NaN. Bit-wise, far from zero, like type 3 with α = β. At |u| < 2⁻¹⁰²² you lose everything. This is the switch “either steps near 1, or a well in 0”. Type 1 — quotient of two steps and arcsin A = (x₀ + small)^(1/p), B = (x₀ + tiny)^(1/q), p = P[r+11], q = P[r+9], y = arcsin(A/B). For x₀ + small > 0 and large p, q: A/B = 1 + ln(x₀ + small)/p − ln(x₀ + tiny)/q + O(p⁻² + q⁻²). Let δ = A/B − 1. The domain of arcsin requires δ ≤ 0 and A/B ≥ −1. The sign of δ is set by the competition p vs q and the shifts small/tiny. This is discrete and from the hash — hence NaN on part of the keys without any “average” continuity. When δ → 0⁻: arcsin(1 + δ) = π/2 − √(−2δ) + O(|δ|^(3/2)). The root of the distance to the edge: this is not a contraction of 1/p. This is a singularity of the derivative d/dδ arcsin(1 + δ) ∼ 1 / √(−2δ) → ∞. Steps of pow in A and B (widths p, q ulps of the argument) after the quotient give δ on a grid of O(2⁻⁵²). Then √(−δ) stretches these steps. Where type 0 suppresses the tail, type 1 at the edge of the domain amplifies this tail — but only on one side, and only when A/B falls into [−1, 1] at all. Bit estimate: far from the edge (|δ| ∼ 10⁻²) arcsin is Lipschitz with constant O(1), so it preserves as many bits of δ as the two pow left, i.e. roughly 53 − log₂ p − log₂ q + log₂ |x₀| minus bits of cancellation in A/B when A ≈ B. At A ≈ B cancellation in the quotient eats −log₂ |δ| bits, and √(−δ) returns part of them. The balance at the edge is therefore unstable in both directions: once it crushes to π/2 (plateau), once it scatters the tail over many ulps of y. Reading this together, since 1–3 are only discreteness of the course the type (switch of the next operation) does not smooth. It divides the axis r / space of the key into four regimes of the seed: * 0 — rail A(r, hash) + steps of pow of width p…2p ulps + coarsening ×8 by ρ. Surviving bits of the seed: ∼ 35…47 out of 53, locally non-monotonic. This is the carrier of the small divergence. * 3 — the same family of steps, without rail and without ρ; jitter finer, body of the result closer to 1. * 2 — like 3, plus a well at x₀ = tiny. * 1 — two independent steps, quotient, then either NaN, or π/2, or √(distance to 1) as an amplifier. In ℝ only what sits above the step is invertible: σ, ρ, 1 + ln z / p. The tail below log₂ p bits of z has no preimage even before rounding of the result; after fl(exp(fl(ln)/p)) there is also no monotonic preimage for bits just above the threshold. For a 53-bit mantissa this threshold in type 0 is precisely k ∼ 50 − log₂(p A), not the limit z^(1/p) → 1. If the next simplification is to be a formula for the expected width of the plateau in ulps of the final result of type 0 as a function of r, it can be written as one expression: W(r) ≈ ulp(ρ + 1 + (ln A)/p) / ulp(A^(1/p)) · p ≈ 8p · A / 2^{e_A} ∈ [4p, 8p], i.e. the final step of type 0 has a width of order 8p ulps of the seed on the scale of z, and this is that graininess which the limit p → ∞ has not yet eaten. The list which comes out of this loop is not a sequence of values of a real function. It is a finite vector of registers. Below I will treat this as an object, perform the composition with base_fraction (appendix #1_return_base_fraction.txt), and show why only an approximation of unknown thickness remains backwards. What is returned After the loop I have Y = (ŷ₀, ŷ₁, …, ŷ_{N−1}) ∈ Fᴺ, N = rounds ∈ {52, …, 68}, i.e. at most 68 binary64 numbers (r = 0, …, N−1; maximum index 67). Each coordinate arises separately from the same seed: ŷᵣ = F̂_{r, typ(k,r)}(f̂(k), k, r, N), where f̂(k) is already not f(k, r_base) from the appendix, only f̂(k) = fl( (fl(k) ⊕ ⊕_{j ε: return 1/√σ else: return 1 EUCLID_ROT16(v, c, s, reason): if reason = "cipher": s ← s / √15 if reason = "decipher": s ← −s / √15 R ← (c, s, s, …, s) // 1 + 15 identical imaginaries return SEDENION_MUL(R, v) // left R·v SEDENION_MUL(R, Q): // Cayley–Dickson, textbook (a,b) ← SPLIT8(R) (c,d) ← SPLIT8(Q) real ← a·c − conj(d)·b imag ← d·a + b·conj(c) return real ∥ imag // 8+8 OKTONION_MUL, QUAT_MUL, CONJ, ADD, SUB - Cayley–Dickson / textbook. Four layers, without algebra beyond the textbook Embedding. vᵢ ∈ {0, …, 2³⁰ − 1}, Mbd = (2³⁰ − 1) · noise. In ℝ (vᵢ · noise + noise/2) / Mbd = (vᵢ + 1/2) / (2³⁰ − 1) ∈ ((1/2)/(2³⁰ − 1), 1]. Enoise cancels. In F multiplication and division do not cancel to the end — a residuum of ulp remains. The middle of the bucket (+1/2) is deliberate: the word goes to the middle of the interval, not to the dyadic edge. QCO (malformed). Hierarchy on indices from the pool, not on positions of the vector: π[0], π[1] — unchanged, π[2], π[3] — (·)², π[4..7] — (·)⁴, π[8..15] — (·)⁸. After embedding all coordinates are in (0, 1], so x ↦ x^{2ᵏ} crushes them toward 0 the more strongly the higher the level. This is that “ever faster loss of resolution of the higher components”: not rotation, only directional truncation of the tail by the power, before the vector even hits the sphere. rev is written, unused here. Scale. After QCO ‖v‖₂ ≠ 1. You divide by the norm and send the scale explicitly. This is not a secret. The demodulator is to receive s separately. After this step v ∈ S¹⁵ only until the first sedenion multiplication. Left R · v. R(φ) = (cos φ, sin φ / √15, …, sin φ / √15), φᵣ = phi_input · ŷᵣ. ‖R‖₂ = 1 in ℝ. Fifteen imaginaries are equal — this is not a general element of S¹⁵, only a ray. The angle φᵣ comes from Flist, i.e. from the HSM bucket, not from a formula in ℝ. The multiplication is textbook C–Dickson 𝕊 = 𝕆 × 𝕆. Sedenions are not a division algebra and do not preserve the norm for arbitrary arguments; after N ∈ {52, …, 68} such products the vector is no longer guaranteed unit. The sent scale anyway comes from before the loop. What comes out msg = { s, v^{(N)} }, where v^{(N)} is the opentext block after embedding, the hierarchy of powers and N left multiplications by R(φᵣ). This is a modulated vector in hypercomplex planes (more strictly: in the left multiplication of the sedenion). The channel gets s and sixteen doubles. It does not get k, it does not get Flist. The composition which noises the tail is already multiple here: residuum of the embedding (Enoise does not cancel in F), powers of QCO on coordinates < 1, then N fl(sin), fl(cos) and the full C–Dickson tree, whose every +,* is Π ∘ G. Higher axes after QCO sit closer to zero, so they fall under ulp faster and further spin as steps, not as coordinates from ℝ. This is the same mechanism as in Flist, only now the carrier is already the block. phi_input = 2π. The angle in round r is therefore φᵣ = 2π · ŷᵣ, i.e. a full rotation scaled by the bucket from Flist. Left R(φᵣ) · v is applied N ∈ {52, …, 68} times, each time on a vector which is already not the previous vector from ℝ. Why fidelity with respect to ℝ falls away from the construction In ℝ left multiplication by R(φ) = (cos φ, sin φ / √15, …, sin φ / √15) is one concrete map Cᵣ: ℝ¹⁶ → ℝ¹⁶. The composition C_{N−1} ∘ ⋯ ∘ C₀ ∘ Q ∘ ι would have a preimage of geometry set by the formula: ι is the embedding (v + 1/2)/(2³⁰ − 1), Q is the hierarchy of powers, Cᵣ is C–Dickson. The tail of the fraction in ŷᵣ would enter cos(2π ŷᵣ) and sin(2π ŷᵣ) as phase. On the machine there is no Cᵣ. There is Ĉᵣ = Π ∘ (C–Dickson) ∘ (fl(cos), fl(sin), fl(1/√15)), and the argument is already ŷᵣ ∈ F, not yᵣ ∈ ℝ. Flist was made so that the mantissa tail of ŷᵣ is full and so that ŷᵣ is not a projection of a real value. 2π ŷᵣ does not fall into a point of the grid at which cos and sin are “the same angle as in ℝ”. You get a different representative of the phase, then a different R, then different sixteen coordinates after the tree 8+8, 4+4, quaternion. One round is already not Cᵣ. N rounds are Ĉ_{N−1} ∘ Π ∘ Ĉ_{N−2} ∘ Π ∘ ⋯ ∘ Π ∘ Ĉ₀, not Π ∘ (C_{N−1} ∘ ⋯ ∘ C₀). Each Π forces the result into a bucket which is not a bucket of a real level set. The next R(φ_{r+1}) multiplies already this bucket, not the image from ℝ. The number of operations is not an ornament. One sedenion multiplication is a C–Dickson tree: two octonions, four quaternions, each quaternion is 16 multiplications and 12 additions. Along the way QCO raised eight axes to the eighth power — these coordinates sit near zero and their ulp is denormal or the last bits of the mantissa. Subsequent + and * with R do not rotate them “in the plane”. They throw them between neighboring doubles. After several dozen rounds the higher axes are a trace of roundings, not an image of Cᵣ ∘ ⋯(v). Hence the sentence which this step needs: with code tables with a full mantissa tail there is no option for multiple left multiplication to behave in F in accordance with ℝ. Not because C–Dickson is “badly implemented”. Because each preimage is truncated by the register and the next round takes already a deformed representative. The composition of additions and multiplications is here a tool of one-wayness on the basis of ℝ, not an error of arithmetic which could be corrected by a longer mantissa in the same formula — a longer mantissa only shifts the threshold, at N ∼ 60 and a full tail of Flist you exceed it again. The sent v^{(N)} is a modulated block. Its relation to the open block is deterministic on this machine and is not reducible to a composition of real rotations by angles 2π yᵣ. Digression if because of the use of cos,sin,sin,sin... one can be fooled (I was fooled) that this is a rotation as in a normed algebra with proper division, but this is a one-parameter, left, irreversible (even before FPU) deformation of the 16-vector, repeated N times with different phi. The word “rotation” looks from the formula, but geometrically it is not a rotation at all, it is a macabre transformation. The earlier use of the QCO pool with exponents 2,4,8 serves so that the tree of operations fits somehow in the fpu mantissa. ================================================================================================ ================================================================================================ #7_sprawdzenie_operacji_.txt ================================================================================================ Next (still on the same machine) we check the operation inverse to the one just performed and we will pack it into a package with additional information about the expected error. // on the sender, right after CRYPT_MSG_V3 ĉ ← CRYPT_MSG_V3(v, H) v̂ ← DECRYPT_MSG_V3(clone(ĉ), H) S ← CHECK_DECIPH(v̂) ĉ.ERR ← (S.NaN_sum, S.ViR_Sum, S.VooRSum) // only counters; indices remain local // package on the channel: { scale, msg, ERR } DECRYPT_MSG_V3(ĉ, H): v ← ĉ.msg s ← ĉ.scale for r = H.Rounds−1 … 0: // reverse order of rounds φ ← 2π · H.Flist[r] v ← EUCLID_ROT16(v, cos φ, sin φ, "decipher") // decipher: sin ← −sin/√15 ⇒ R̄ = (cos, −s, …, −s) v ← v / s v ← QCO_LIKE(v, "rev", H.QCOpool) // x ↦ x^{1/p} on the same π[i] for i = 0 … 15: v_i ← floor( (v_i · H.Mbd) / H.Enoise ) return v CHECK_DECIPH(v): // num_30_max = 2³⁰−1 NaN ← in ← out ← 0 for i = 0 … 15: if is_nan(v_i): NaN++ else if 0 < v_i < 2³⁰−1: in++ // strict; 0 and 2³⁰−1 are “out” else: out++ return counters (+ tables of indices locally) Inverse of the embedding in ℝ: floor( ((vᵢ + 1/2) / (2³⁰ − 1)) · (2³⁰ − 1) ) = floor(vᵢ + 1/2) = vᵢ for integer vᵢ. Enoise cancels again. In F — after N rounds and QCO — it does not. What this “inverse” really inverts The order is mirrored: Ĉ₀⁻ ∘ ⋯ ∘ Ĉ_{N−1}⁻ ∘ (· / s) ∘ Q^rev ∘ floor(· Mbd / noise). Ĉᵣ⁻ is the same C–Dickson tree with opposite imaginary. In a normed algebra left multiplication by R⁻¹ = R̅ undoes R · v. Sedenions are not a division algebra; R̅ · (R · v) is not the identity even in ℝ. On the machine there is additionally that v on the input of the backtracking is already Π(R · v_pop), and cos(2π ŷᵣ), sin(2π ŷᵣ) are calculated anew, not stored from the forward path. This is not a pair (U, U⁻¹). This is a pair of two independent projections of the same formula. QCO rev is algebraically x^{1/p} after x^p. For x ∈ (0, 1] in ℝ it returns. In F the eighth power swept the tail toward 0; the eighth root does not reconstruct bits which the grid no longer holds. floor at the end is a hard integer bucket: everything which along the way fell out of (0, 2³⁰ − 1) remains as out or NaN. The local round-trip therefore measures not “whether the code works”, only how much this machine, with this k and this block, does not close the loop. Protocol ERR Three numbers, without indices: ERR = (#NaN, #in range, #out of range), #NaN + #in + #out = 16. The range is open: 0 and 2³⁰ − 1 count as out. This is deliberate — the edges of the 30-bit grid are treated as damage, not as a legal word. ERR goes in the package openly, next to scale and msg. The recipient with his own call ν = 1 (or the same material, when the prototype is symmetric) does the same backtracking and gets his vector of words. The sender does not hide how many axes fell locally, how many returned to the legal 30-bit interval, how many flew out. This is a report of the expected discrepancy, not a checksum. A checksum would claim “the block is supposed to be such”. ERR claims “on this machine, with this code table, the loop closes with such a distribution of buckets”. Package leaving this machine: { scale, msg, ERR } Why this leaves by the open channel The adversary sees (s, v^{(N)}, ERR). He therefore sees not only the modulated vector, but also the local characteristic of the sender’s deformation: how many axes died in NaN, how many still sit in the word grid, how many fell outside it. This is a function of Flist, QCO and N rounds on his FPU and this block. This will be essential when injecting false blocks. ================================================================================================ ================================================================================================ #8_wstrzykiwanie_bloków_fałszywych.txt ================================================================================================ In the loop generation block by block on the basis of already revealed pseudocode (no new operation) false blocks are injected, which with the use of the true key with all certainty the legal recipient (not an attacker brute-forcing i.e. not possessing every key as the test omnipotent adversary in the process) will not open with his key, which we check at the attempt to inject a fake. INFUSE_FALSE_RANDOM_BLOCKS(): n ← RANDOM_INT(0, R_fake − ⌊R_fake/2⌋) // how many fakes in this portion for f = 1 … n: buff ← RANDOM_BUFFER_480() // prototype: i.i.d.; bit statistics — separate matter v ← BUFF480_TO_VEC16(buff) H_f ← HMS_CALL("fake_0") // different k than current_0; k_f dies with the block ĉ_f ← CRYPT_MSG_V3(v, H_f) v_f ← DECRYPT_MSG_V3(ĉ_f, H_f) E_f ← CHECK_DECIPH(v_f) // ERR with its own (false) key H_0 ← HMS_CALL("current_0") // true key of the legal recipient v_0 ← DECRYPT_MSG_V3(ĉ_f, H_0) E_0 ← CHECK_DECIPH(v_0) // ERR when the same msg backtracks key 0 if E_f = E_0: // collision of the ERR protocol skip block // do not inject // log of interference — only test debug; not part of the channel else: ĉ_f.ERR ← E_f // explicit report from the false key PACK["block_" + |PACK|] ← ĉ_f fake_blocks_left++ // optionally, exclusively harness Eve: remember k_f What is injected A false block is a legal package of the same type as a true one: ĉ_f = {scale, msg, ERR_f}. It arose from a different k_f (fake_0) and from a different buffer. On the channel one cannot distinguish it by format. One can distinguish it by the key. The legal recipient has only current_0. On ĉ_f he will perform DECRYPT(ĉ_f, H₀) and will get a vector whose CHECK gives ERR₀. The sieve at injection guarantees ERR_f ≠ ERR₀. That is: the report which travels in the package is not the report which arises with the true key. The recipient either compares ERR and rejects the block, or assembles the words and gets NaN / outside {1, …, 2³⁰ − 2} — in both cases he will not “open” the block. This is the certainty from the code, not cryptographic certainty against someone who has the whole bunch. Why the sieve E_f ≠ E₀ ERR is an open attack on the space of guesses. If ERR_f = ERR₀, the fake would carry the same public trace of deformation as with the legal key. Then the protocol which is to cut the adversary would stop cutting this block: the distribution (#NaN, #in, #out) would not betray that the seed was different. Collision of three small counters (adding up to 16) is rare, but possible. On a hit the block is abandoned, not repaired. You do not select ERR. You do not adjust the buffer. You throw out the sample and go further. In the package remain only those fakes for which the true key does not reproduce the attached report. An omnipotent test adversary who would have received every k_f will open these blocks — this is a harness, not the channel. The legal recipient of this bunch does not have it and has no reason to have it: a fake is not a message for him. Place in the stream Generation goes block by block. This function inserts n decoys into temp_encrypt_pack next to the blocks from current_0. The number block_m does not carry a “fake” marker. The marker is only in the testharness (_test_sequence_E_fake, _fake_blocks_left). The channel sees a homogeneous sequence {scale, msg, ERR}_m. Part of them close the loop on H₀. Part close the loop on some H_f and on H₀ give a different ERR. The recipient with one key opens the first part and rejects the second. This is everything which this loop adds to the already written-out operations. ================================================================================================ ================================================================================================ #9_rotacja_kluczy.txt ================================================================================================ If we introduce a true block (not a false one), we still perform the operation of calling key rotation in the hardware security module; We will call key 1 (normally for cipher/decipher we call the zero one); with the data from this key (normal package from HSM) we will decode the block which we just encoded with the proper key; this key is DEFINITELY WRONG; but the recipient also has it at position 1; because we have the same data in the HSM; and we will extract some numbers dependent on the encryption and the open text which we sent; and if this is a sufficient number of numbers we will perform key rotation; This is not a new modulation. The true block already lies in _cipher. Key 1 is deliberately wrong for opening this block. I use it as a source of numbers for rotating the bunch, not as a decoding key. The recipient has the same bunch, so upon recognizing the block as true he will perform the same. // after CRYPT + ERR from current_0, true block (not decoy) H₁ ← HMS_CALL("current_1") // package from position 1; not used for cipher v₁ ← DECRYPT_MSG_V3(clone(cipher), H₁) // backtracking with WRONG material G ← EXTRACT_NUMS(v₁) // non-NaN coordinates, in axis order if |G| > 1: HMS_CALL("rotate_keyring", G) EXTRACT_NUMS(v): G ← ∅ for i = 0 … 15: if ¬is_nan(v_i): G["_" + |G|] ← v_i // also outside {1,…,2³⁰−2}; only NaN drops out return G ROTATE_KEYRING(G): // called only when |G| ≥ 2 m ← |KeyRing| // _KRmod M ← 2⁵² − 1 // _key_max, not 2⁵³−1 t ← |G| for i = 0 … t−2: // pairs (Gᵢ, Gᵢ₊₁) c₀ ← ⌊Gᵢ⌋ c₁ ← ⌊Gᵢ₊₁⌋ p₀ ← ⌊|Gᵢ₊₁|⌋ mod m // new position of former key 0 p₁ ← ⌊|Gᵢ⌋⌋ mod m // new position of former key 1 if p₀ = p₁: p₁ ← ¬p₁ // 0↔1; slot collision K₀ ← KeyRing[_0]; remove _0 K₁ ← KeyRing[_1]; remove _1 K₀ ← KEY_ADD(K₀, M, c₀) K₁ ← KEY_ADD(K₁, M, c₁) // shift of the prefix 1..p₀ by one downward (toward _0) for k = 1 … p₀: if KeyRing[_k] exists: KeyRing[_k−1] ← KeyRing[_k] remove _k KeyRing[_p₀] ← K₀ // the same for p₁, already on the modified bunch for k = 1 … p₁: if KeyRing[_k] exists: KeyRing[_k−1] ← KeyRing[_k] remove _k KeyRing[_p₁] ← K₁ The true block goes to temp_encrypt_pack as always. Rotation of the bunch is a side effect after packing this block, on the sender’s HSM. The recipient will repeat it only when in his process he recognizes the block as true. KEY_ADD. This is not modular addition. It is an asymmetric shift with reflection only from below. KEY_ADD(K, M, c): // M = 2⁵²−1, c = ⌊Gᵢ⌋ or ⌊Gᵢ₊₁⌋ s ← sign(c) // −1 / 0 / +1 K' ← K − c if s < 0: K' ← K' − c // i.e. K' ← K − 2c = K + 2|c| while K' < 2: K' ← M − K' return K' Three branches: K′ = | K − c | c > 0, | K | c = 0, | K + 2|c| | c < 0. Then, and only then, if the result fell below 2: K′ ← M − K′ = (2⁵² − 1) − K′. The loop in practice does at most one step: for K′ ∈ {1, 0} ∪ (−∞, 0) the result is already ≥ 2. There is no upper reflection. K′ > M is a legal output. What follows from this for the bunch c comes from the wrong backtracking on key 1: it happens to be in the word grid, happens to be negative, happens to be of order far beyond 2³⁰. The sign of c is not “direction on the module”. Negative c enlarges the key twice as strongly as positive decreases it. The lower threshold K ≥ 2 is hard — the key never returns as 0 or 1 (important for splitmix and for k > 0 in base_fraction). The upper threshold M is only a mirror wall at reflection from below, not a ring ℤ/Mℤ. After rotation the new K₀, K₁ are therefore * the old keys, * shifted by these raw ⌊Gᵢ⌋, * possibly once reflected from 2 by M − K′, * inserted into slots from |G| mod m. The recipient, with the same G, gets bit-wise the same K′. Not because this is a cyclic group, only because the same sequence −, sign and while on the same machine. ---------------------------------------- Where the numbers come from current_1 is a normal HSM package: own Flist, Mbd, QCOpool, N. You backtrack with it the vector which arose from current_0. In ℝ this does not make sense as decoding. In F you get sixteen registers: part NaN, part numbers (in the word range or outside). G is those which remained numbers. You do not filter them by the 30-bit grid. To the rotation goes the raw trace of the wrong backtracking — i.e. a mixture of * the open block (through v^{(N)}), * the code table of key 0 (because msg arose from it), * the code table of key 1 (because you backtrack with it), * truncated preimages of both chains. One non-NaN coordinate is not enough: there is no pair (Gᵢ, Gᵢ₊₁). Zero or one number ⇒ the bunch stands. Two and more ⇒ as many pairs as neighbors in G. What a pair does with the bunch From a pair (a, b): K₀ ← K₀ ⊕_M ⌊a⌋, goes to slot ⌊|b|⌋ mod m, K₁ ← K₁ ⊕_M ⌊b⌋, goes to slot ⌊|a|⌋ mod m. If both slots fell the same, the second jumps to ¬p (at m = 2 always to the free one from {0, 1}). The prefix of the ring is pushed downward, so as to insert the mutated keys at the calculated positions. At the next pair you work already on the bunch after the previous pair. After rotation “the key to the block” no longer necessarily sits on _0. The next true block calls again current_0 — whatever now lies on _0. Fakes do not do this rotation: by sieve they are not true, so they are not allowed to move the bunch. Why both HSMs will do the same The same starting bunch, the same block, the same call "current_1", the same arithmetic machine ⇒ the same v₁, the same G, the same rotation. The condition is one: the recipient must first recognize the block as true. A fake also has ERR and msg; launching rotation on it would derail the bunches. Therefore rotation is tied to the path “true block”, not to the mere fact of possessing a package. On the channel neither G nor the rotation command travels. An ordinary {scale, msg, ERR} travels. Synchronization of the bunch is a consequence of the common preimage of the wrong key on the common block. Until the moment when you describe recognition of truth on the recipient’s side, the sentence suffices: if both recognized the block as true, both turned the bunch with the same pair of numbers from position 1. The starting bunch {K₀, K₁, …} is not a session key. It is a seed. A true block does three things at once, already written out: 1. modulates the opentext with material from the current _0, 2. backtracks the same msg with material from the current _1 and collects non-NaN into G, 3. if |G| > 1, mutates and rearranges the bunch through pairs (Gᵢ, Gᵢ₊₁). After this step what lies in the module is no longer the seed. There lies the image Ring_{n+1} = R(Ring_n, G(K₀^{(n)}, K₁^{(n)}, opentext_n)). G depends on both starting positions and on the block which just went into the channel. The next true block calls again "current_0", but this is already a different register: the one which the rotation inserted onto _0. A fake does not move the bunch, so the chain of keys leads exclusively along blocks recognized as true. The condition |G| > 1 is a fuse, not an exception from the principle. Below two numbers there is no pair — the seed (or the current bunch) remains, the block still departs. Above: from the first such true block the session is a function of the seed and of the opentext, not of the seed alone. A recipient with the same seed and the same sequence of recognized blocks reconstructs the same bunch. Whoever has only the seed and the stream of packages, and does not distinguish truth from decoy, goes a different path of rotation or none. ================================================================================================ ================================================================================================ #10_cała_pętla_szyfrująca.txt ================================================================================================ The loop assembles the already written-out stages into one output buffer and descends by a block. The test harness, Eve and file writing do not belong to the channel — below only what builds the package. PERFORM_ENTROPMENT(): // called every frame / every tick if ¬ongoing: exit if blocks_left = 0: if test: hide PACK + sequences real/fake else: write PACK as JSON CLEANUP; exit if blocks_left = blocks_num: // first tick of the session HMS_CALL("history_make") // snapshot of the seed PACK ← ∅ // fake_blocks_counter — explained at the bottom; // - tick of one true block - INFUSE_FALSE_RANDOM_BLOCKS() // decoys BEFORE i ← blocks_num − blocks_left // 0,1,2,… buff ← BITS_RAW[480i … 480i+479] v ← BUFF480_TO_VEC16(buff) H₀ ← HMS_CALL("current_0") ĉ ← CRYPT_MSG_V3(v, H₀) ĉ.ERR ← CHECK(DECRYPT(ĉ, H₀)) PACK["block_" + |PACK|] ← ĉ // next slot, without true/false marker H₁ ← HMS_CALL("current_1") G ← EXTRACT_NUMS(DECRYPT(ĉ, H₁)) if |G| > 1: HMS_CALL("rotate_keyring", G) // seed ceases to be the seed remember index as real // only testharness INFUSE_FALSE_RANDOM_BLOCKS() // decoys AFTER blocks_left ← blocks_left − 1 Output of the session: one object PACK = {block_0, block_1, …}, each entry is {scale, msg, ERR}. Order in the package = order of injections, not the logical number of the opentext block. Between two true ones there may lie zero or several decoys (sieve E_f ≠ E₀). What this loop does The bit buffer is already cut into B = blocks_num segments of 480. A tick takes one segment, wraps it with material from the current _0, attaches the explicit ERR from the local round-trip and throws it into PACK. Then, on the same msg, wrong backtracking from _1 possibly turns the bunch. The next true segment already goes on the rotated _0. Decoys are on both sides of the true block. They do not move the bunch. The legal recipient, when he gets to unpacking, receives a homogeneous sequence of packages and must himself separate those which close the loop on his current _0 from those which close it on a foreign k_f. Writing JSON / test snapshot is only a dump of PACK after the last tick. The algorithm ends on this: the whole buffer sits in the blocks, the HSM seed has been consumed in the rotations at true blocks, the channel received neither k, nor G, nor true/false markers. The decoy counter is a paranoia slider: how many fakes per true block. To the algorithm belongs exclusively that INFUSE exists and that the sieve E_f ≠ E₀ already at the sender rejects the ERR collision. ================================================================================================ ================================================================================================ #11_pętla deszyfrująca.txt ================================================================================================ The recipient’s loop is mirrored. The only new predicate: AO_check. AO_CHECK(S, ERR): return 1 if (S.NaN, S.in, S.out) = (ERR[0], ERR[1], ERR[2]) else 0 PERFORM_TRANSCENDO(): // tick after tick over PACK if ¬ongoing: exit if blocks_left = 0: assemble bits → text; write; CLEANUP; exit if blocks_left = blocks_num: // first tick HMS_CALL("history_make") // the same seed as at the sender BUF ← ∅ i ← blocks_num − blocks_left ĉ ← PACK["block_" + i] // package order, not “logical” ERR ← ĉ.ERR H₀ ← HMS_CALL("current_0") v̂ ← DECRYPT_MSG_V3(ĉ, H₀) S ← CHECK_DECIPH(v̂) if AO_CHECK(S, ERR): // true block BUF ← BUF ∥ VEC16_TO_BUFF480(v̂) H₁ ← HMS_CALL("current_1") G ← EXTRACT_NUMS(DECRYPT(clone(ĉ), H₁)) if |G| > 1: HMS_CALL("rotate_keyring", G) // the same G, the same bunch else: skip // decoy; do not move the bunch; here for the purposes of side channel attack an identical operation can be performed as under the condition fulfilled, but the signal to the hardware security module will not move the true bunch of keys, the goal will be to obtain pwr consumption and timing identical with recognition of a proper block, if this level of paranoia were essential in the implementation; blocks_left ← blocks_left − 1 AO_check compares the local distribution of buckets after backtracking with the current _0 with the explicit ERR from the package. Agreement = this block arose on the material which now lies with you on _0. Disagreement = different k, or a collision which the sender already did not let in. Mirror Sender: decoy / truth / decoy; rotation only on truth. Recipient: goes linearly over block_0, block_1, …; rotation only when AO_check = 1. The same seed, the same order of true blocks, the same pairs G ⇒ the same bunch after each accepted block. A decoy does not supply G and must not supply it: one turn on a fake permanently derails the rings. ERR is part of the carrier. It is not a checksum of the opentext. It is the imprint of the deformation of this HSM, this _0, this msg. The sender calculated it on H₀ (truth) or on H_f (decoy) and let the decoy in only when E_f ≠ E₀. The recipient does not guess which entry is truth. He asks his own automaton. Where security comes from, since the seed is short The starting bunch is a few numbers ≤ 2⁵² − 1. After the first accepted block it is no longer a session secret — it is a seed of the chain Ring_{n+1} = R(Ring_n, G(K₀^{(n)}, K₁^{(n)}, opentext_n)). K₁ never enters CRYPT. It is not in the Flist of the carrier, it is not in msg, it is not in ERR. The trace remains exclusively in G, and G does not travel the channel: it arises locally, from msg and from the secret _1. After use K₁ is mutated together with the rest of the bunch. The public material of the block does not carry position 1. Decoys are the user’s choice so that analytically they do not stand out from truth (the same format, the same ERR as a triple of counters, the same shape of msg). Eve gets B_real + B_fake puzzles. Cost: * filter out decoys without _0 — searching divisions of the sequence, * compose which entries are the message — C(B_real + B_fake, B_real) paths, of which almost all will spoil the rotation at the first error, * hit 53 bits of the seed and maintain synchronization of the bunch through the whole message. Each successive true block multiplies this cost, because the bunch depends on the content of already read blocks. The length of the opentext is here the carrier of security. The seed does not have to be large: after the first block the session is not a function of the seed alone. K₁ remains secret, does not produce a cipher and after use disappears in the rotation. A recipient with the seed and with AO_check pays as much as the package has ticks. An attacker without the seed — or with the seed, but without distinguishing truth — pays for every hypothesis of division and for every wrong turn of the bunch, which he will not reverse without re-traversing the whole chain. ================================================================================================ ================================================================================================ #12_rachunek_FPU_technikalia.txt ================================================================================================ DIDACTIC NOTE · NUMBER REPRESENTATION IEEE 754 - bit record Layout of bits in memory, characteristic and mantissa, hidden bit, decoding to a real number and special codes (signed zeros, infinities, NaN, subnormal numbers). The goal is to show what follows from what: why the fields have exactly such a width, what bias is for, what the hidden bit is for and why Inf/NaN “sit” on the extreme values of the characteristic. 1. Idea: a real number as a pair (mantissa, exponent) A computer does not hold a “decimal point”. It holds a finite sequence of bits. IEEE 754 is an agreement on how to interpret this sequence as an approximation of a real number ℝ. The agreement is analogous to scientific notation, only in base 2: value v = (−1)^s · m · 2^E where s ∈ {0, 1} is the sign, m is the significand (mantissa / significand), and E is the integer exponent (characteristic after subtracting the bias). This is the interpretation 𝔽 → ℝ: from three bit fields we read s, m, E and substitute into the formula. Two independent goals of the format design: (1) large range — large |E|; (2) high precision — many bits of m. With a fixed bit budget one is bought at the cost of the other. Hence different widths: binary16 / binary32 / binary64. 2. Layout of bits in memory (logical) In the standard bits are numbered from the most significant. First the sign, then the characteristic (exponent with bias), at the end the fraction of the mantissa. This is a logical layout — independent of whether the processor writes bytes in memory little-endian or big-endian. binary32 · single precision · float · 32 bits Bits 31 … 0 (MSB on the left): sign S characteristic E_bias (8 bits) fraction f / mantissa (23 bits) bit 31 bits 30 … 23 bits 22 … 0 Example pattern (field labels only, not a value): S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF binary64 · double precision · double · 64 bits S characteristic E_bias (11 bits) fraction f (52 bits) 63 bits 62 … 52 bits 51 … 0 Comparison of binary formats Format Width Characteristic Fraction f Precision p Bias binary16 (half) 16 5 bits 10 bits 11 = 10+hidden 15 binary32 (single) 32 8 bits 23 bits 24 = 23+hidden 127 binary64 (double) 64 11 bits 52 bits 53 = 52+hidden 1023 binary128 (quad) 128 15 bits 112 bits 113 = 112+hidden 16383 Precision p is the number of significand bits including the hidden bit. Therefore one says “float has ~7–8 decimal digits” (24 · log₁₀ 2 ≈ 7.22), and “double ~16 digits” (53 · log₁₀ 2 ≈ 15.95). Byte order in RAM is a separate matter The layout S | characteristic | f describes the meaning of bits, not addressing. On a little-endian machine (x86, typical ARM) the youngest byte lies at the lowest address. Therefore a memory dump of a 32-bit float 1.0 looks as 00 00 80 3F, not 3F 80 00 00. In bit analysis always first assemble the word, then divide the fields from the MSB. 3. Three fields and their meaning Sign bit S S = 0 → non-negative number; S = 1 → non-positive number. Zero has two representations: +0 and −0. For finite ordinary numbers v = (−1)^S · |v|. The sign is not a two’s-complement code of the whole number — it concerns exclusively the polarity of the value after composing the characteristic and mantissa. Characteristic (exponent field) e = E_bias The field e is an unsigned integer. E is not held in two’s complement, because then comparison and sorting of bit patterns would be inconvenient, and extreme patterns (all zeros / all ones) would be harder to reserve for special codes. Instead: e = E + bias, bias = 2^{k−1} − 1 For k = 8 (float): bias = 127. For k = 11 (double): bias = 1023. Ordinary range e ∈ {1, 2, …, 2^k − 2}. Two extremes are reserved: Field e Meaning What it codes e = 0 (000…0) lower reserve ±0 and subnormal numbers 1 ≤ e ≤ 2^k − 2 normal numbers E = e − bias, m = 1.f e = 2^k − 1 (111…1) upper reserve ±∞ when f = 0; NaN when f ≠ 0 Fraction f and mantissa m The field f is the bits after the binary point. The significand itself arises only after adding the hidden bit (for normal numbers) or zero (for subnormals): normal number: m = 1.f₂ i.e. m = 1 + f / 2^t subnormal number: m = 0.f₂ i.e. m = 0 + f / 2^t where t is the number of bits of the field f (23 or 52). Hence 1 ≤ m < 2 for normals and 0 ≤ m < 1 for subnormals. 4. Hidden bit — where it comes from and what for A binary number can always be shifted so that the first significant one stands just before the point: 1101,011₂ = 1,101011₂ × 2³ This is normalization. After normalization the leading bit is always 1 — so one does not have to store it. We store only the tail f. On reading we prepend “1.”. Gain: +1 bit of precision for free (float: 23 stored → 24 significant; double: 52 → 53). The hidden bit disappears in two situations, when normalization is impossible or senseless: 1. Zero — there is no one whose position one could seek. 2. Subnormals — the exponent is already at the bottom (E = E_min), and the number is smaller than 1.0 × 2^{E_min}. Then the leading bit = 0 and precision gradually falls. 3. Inf / NaN — these are not finite values with a mantissa in the sense of 1.f. Constructional conclusion The hidden bit exists only because a unique, normalized form 1.f × 2^E was chosen. Uniqueness of representation (almost always one bit pattern ↔ one value) simplifies comparisons and hardware. The price is the necessity of a separate path for subnormals and special codes. 5. Full interpretation of bits → value in ℝ Let k = width of the characteristic, t = width of the fraction, bias = 2^{k−1} − 1, E_min = 1 − bias. For binary32: k = 8, t = 23, bias = 127, E_min = −126, E_max = 127. For binary64: k = 11, t = 52, bias = 1023, E_min = −1022, E_max = 1023. Five decoding rules (binary32; analogously for others) Condition on e, f Value v 0 < e < 255 v = (−1)^S · 2^{e−127} · (1 + f/2²³) normal e = 0, f ≠ 0 v = (−1)^S · 2^{−126} · (0 + f/2²³) subnormal e = 0, f = 0 v = (−1)^S · 0 signed zero e = 255, f = 0 v = (−1)^S · ∞ infinity e = 255, f ≠ 0 v = NaN (quiet or signaling; S usually ignored) Note the exponent of subnormals: this is not e − 127 = −127, only the rigid 2^{−126}. Thanks to this the smallest subnormal meets the smallest normal without a hole in the grid (gradual underflow). Smallest positive normal float: 1.0 × 2^{−126} ≈ 1.175 × 10^{−38}. Smallest positive subnormal float: 2^{−23} × 2^{−126} = 2^{−149} ≈ 1.4 × 10^{−45}. Largest finite float: (2 − 2^{−23}) × 2^{127} ≈ 3.403 × 10^{38}. 6. Calculation from number to bits and back Example A. Encode 12.375₁₀ as binary32 Step 1 — sign. 12.375 > 0, so S = 0. Step 2 — integer part to binary: 12 = 1100₂. Step 3 — fractional part: 0.375 × 2 = 0.75 → 0; 0.75 × 2 = 1.5 → 1; 0.5 × 2 = 1.0 → 1. Hence 0.375 = 0.011₂. Together: 12.375 = 1100.011₂. Step 4 — normalization: we shift the point by 3 positions to the left: 1100.011₂ = 1.100011₂ × 2³ Hence E = 3, m = 1.100011. The hidden bit is the leading 1; into the field f goes the tail 100011 and padding with zeros to 23 bits: f = 10001100000000000000000 Step 5 — characteristic with bias: e = E + 127 = 130 = 10000010₂. Step 6 — we glue the fields: S characteristic e fraction f 0 10000010 10001100000000000000000 Continuous record: 0100 0001 0100 0110 0000 0000 0000 0000 Grouping by 4 facilitates hex: 0x41460000. Backward verification: e = 130₁₀ → E = 3; 1.f = 1.100011₂ = 1 + 1/2 + 1/32 + 1/64 = 1 + 0.5 + 0.03125 + 0.015625 = 1.546875; v = 1.546875 × 8 = 12.375. It agrees exactly — the number is dyadic (denominator a power of two). Example B. Encode −0.75₁₀ as binary32 S = 1. 0.75 = 0.11₂. Normalization: 0.11₂ = 1.1₂ × 2^{−1}. E = −1, e = −1 + 127 = 126 = 01111110₂. f = 10000000000000000000000 (tail after the hidden 1). S characteristic e fraction f 1 01111110 10000000000000000000000 Hex: 0xBF400000. Check: 1.1₂ = 1.5; 1.5 × 2^{−1} = 0.75; minus sign. Example C. 1.0 and 2.0 — the characteristic alone is visible 1.0 = 1.000… × 2⁰ → S = 0, e = 127 = 01111111, f = 0. Hex: 0x3F800000. 2.0 = 1.000… × 2¹ → S = 0, e = 128 = 10000000, f = 0. Hex: 0x40000000. Conclusion: with mantissa 1.0 the characteristic field alone shifts the point. Therefore neighboring powers of two differ exclusively in the characteristic. Between 2^n and 2^{n+1} the spacing (ulp) is constant and equals 2^{n−23} for float. Example D. Decoding a ready pattern Given binary32: 1 10000001 10100000000000000000000 S = 1 → minus. e = 10000001₂ = 129₁₀ → E = 129 − 127 = 2. f = 101000… → 1.f = 1.101₂ = 1 + 1/2 + 1/8 = 1.625. v = −1.625 × 2² = −6.5. Example E. 0.1₁₀ has no finite binary record 0.1₁₀ × 2 = 0.2 → 0; 0.2 × 2 = 0.4 → 0; 0.4 × 2 = 0.8 → 0; 0.8 × 2 = 1.6 → 1; 0.6 × 2 = 1.2 → 1; 0.2 × 2 = 0.4 → … and the cycle 0011 repeats. After normalization: 0.1₁₀ ≈ 1.1001100110011…₂ × 2^{−4} e = −4 + 127 = 123 = 01111011₂. The field f truncates and rounds the tail of the period. Therefore 0.1 + 0.2 ≠ 0.3 in float/double: each of these values is already an approximation, and the sum of approximations does not hit the approximation of 0.3. This is not an error of a “bad standard”, only a necessary consequence of finite f for a number outside the dyadic grid. Example F. The same calculation in binary64 (sketch) For 12.375 nothing changes in S, E, m — only f has 52 zero bits after 100011, and e has 11 bits: E + 1023 = 1026 = 10000000010₂. Hex double: 0x4028C00000000000. Greater width of f does not improve this particular number (it was already exact), but reduces the representation error of numbers such as 0.1. 7. Special records Extreme values of the characteristic do not code “an even larger E”. They code states outside finite normal numbers. This is a deliberate decision: better to have Inf/NaN in the algebra than silent wrapping or a processor trap at every overflow. Signed zeros Value S characteristic f +0 0 00000000 000…0 −0 1 00000000 000…0 +0 and −0 compare as equal (0 == −0 is true), but the sign survives in some operations: 1/(+0) = +∞, 1/(−0) = −∞. Copying the sign (copysign) also distinguishes them. Hex float: +0 = 0x00000000, −0 = 0x80000000. Infinities Value S characteristic f +∞ 0 11111111 000…0 −∞ 1 11111111 000…0 They arise at overflow (result > MAX) and at division of a finite non-zero by zero. Hex float: +Inf = 0x7F800000, −Inf = 0xFF800000. In double the characteristic is eleven ones (0x7FF0000000000000). Arithmetic with Inf is defined: ∞ + 5 = ∞, ∞ × ∞ = ∞, 5/∞ = 0. Whereas ∞ − ∞ and 0/0 give NaN — these are indeterminate expressions, not “very large”. NaN - Not a Number Condition: characteristic = all ones AND f ≠ 0. The sign bit does not carry mathematical meaning (it is sometimes used privately). Inside f the standard distinguishes: Quiet NaN (qNaN) — usually the oldest bit of f (bit 22 in float, bit 51 in double) = 1. It passes through operations and “infects” the result. Typical consequence of 0/0, sqrt(−1), Inf − Inf. Signaling NaN (sNaN) — the same bit = 0, but f still ≠ 0. It is to raise an exception if used in an operation. It serves among other things to detect uninitialized data. Kind S characteristic float f (idea) qNaN (example) 0 or 1 11111111 1xxxx… (bit 22 = 1) sNaN (example) 0 or 1 11111111 0xxxx… ≠ 0 Canonical qNaN of float often met in practice: 0x7FC00000 (S = 0, e = 255, bit 22 = 1, rest zeros). NaN is not equal even to itself: NaN == NaN is false. The test is isnan / f ≠ f. Why do Inf and NaN share the same characteristic? Because after E_max there is no longer a legal exponent. One guard pattern (e = 2^k − 1) is divided by f: empty f = “we went outside the range, but we know the direction”, non-empty f = “the result is not a number”. Subnormal numbers (denormal) When e = 0 and f ≠ 0: hidden bit = 0, rigid exponent E = E_min. Example float, smallest positive: S characteristic f 0 00000000 00000000000000000000001 v = + 2^{−126} × 2^{−23} = 2^{−149}. Hex: 0x00000001. What this is for: without subnormals between 0 and 2^{E_min} there would be a gap larger than the spacings just above 2^{E_min}. Gradual underflow preserves the property: x ≠ y ⇒ x − y ≠ 0 (for values representable in this range, with reservations of roundings). Cost: slower path in the FPU and decreasing precision (the more leading zeros in f, the fewer significant bits). 8. What follows from what — map of causes Why exactly these field widths? 32 bits were a natural machine word. 8 bits of characteristic give E from −126 to 127, i.e. a range of about 10^{±38} — sufficient for engineering physics. The rest (23+hidden) gives ~7 digits. 64 bits: 11 bits of characteristic → 10^{±308}, 52+hidden → ~16 digits, which covers most scientific calculation. This is a compromise, not a law of nature. Why bias, not two’s complement of the exponent? With bias all finite numbers (including ±0 and subnormals) are ordered so that comparison of bit patterns as unsigned numbers almost agrees with comparison of values — it suffices to handle the sign separately. Moreover 000…0 and 111…1 become convenient sentinels at the ends of the range of e. Why does the hidden bit force special codes? If we always prepend a leading 1, zero cannot be written. An exception is needed. The cheapest exception: take e = 0 and say “then the leading bit = 0”. Symmetrically e = max we take for Inf/NaN, because there would not have been room for E_max + 1 without widening the field. Why is the grid not uniform? The spacing between neighboring numbers (ulp) doubles at each successive power of two. Near 1 the ulp of float is 2^{−23} ≈ 1.19 · 10^{−7}. Near 2^{23} ≈ 8.4 million the ulp of float is already 1 — successive integers cannot be distinguished. Hence the classic effect: (float)16777216 + 1 == 16777216. This is not a “broken plus”, only an exhausted mantissa. What the format says, and what it does not say The format establishes only encoding and — in the full standard — rounding rules, exceptions (invalid, overflow, underflow, divide-by-zero, inexact) and operations. It does not establish that the result of your algorithm is exact. Each operation can add at most ½ ulp of rounding error; composition of operations composes errors. Numerical analysis begins where the bit record itself ends. 9. Decoding cheat sheet (binary32) Data Formula / action S = bit 31 sign = (−1)^S e = bits 30…23 as uint if 1…254: E = e − 127 f = bits 22…0 as uint for normal: m = 1 + f/2²³ 1 ≤ e ≤ 254 v = (−1)^S · m · 2^E e = 0, f = 0 v = (−1)^S · 0 e = 0, f ≠ 0 v = (−1)^S · (f/2²³) · 2^{−126} e = 255, f = 0 v = (−1)^S · ∞ e = 255, f ≠ 0 v = NaN Quick hex patterns (float) Value logical pattern hex float +0 0 00000000 000…0 00000000 −0 1 00000000 000…0 80000000 +1.0 0 01111111 000…0 3F800000 −1.0 1 01111111 000…0 BF800000 +2.0 0 10000000 000…0 40000000 +0.5 0 01111110 000…0 3F000000 +12.375 0 10000010 1000110…0 41460000 −0.75 1 01111110 100…0 BF400000 +∞ 0 11111111 000…0 7F800000 −∞ 1 11111111 000…0 FF800000 qNaN (typical) 0 11111111 100…0 7FC00000 min. subnorm. > 0 0 00000000 000…1 00000001 min. normal > 0 0 00000001 000…0 00800000 max. finite 0 11111110 111…1 7F7FFFFF 10. Mini manual algorithm (both ways) Decimal number → bits 1. Write the sign S. 2. Convert |x| to binary (integer part by successive /2, fraction by successive ×2). 3. Normalize to the form 1.f × 2^E. If |x| = 0, then e = 0, f = 0 and end. 4. If E < E_min — underflow: either subnormal (shift right, hidden bit = 0, e = 0), or zero. 5. If E > E_max — overflow → Inf of sign S. 6. e = E + bias. Append f to t bits (round; by default round-to-nearest, ties-to-even). 7. Glue S | e | f. Bits → number 1. Separate S, e, f. 2. Recognize the class according to the table from §5 (normal / sub / 0 / Inf / NaN). 3. Compute m (1.f or 0.f) as the sum of bits · 2^{−i}. 4. Compute 2^E and compose v = (−1)^S · m · 2^E. 11. One sentence to remember IEEE 754 in one breath The sign is 1 bit of polarity; the characteristic is the exponent shifted by the bias, with two guard patterns; the mantissa is the tail after the hidden one. The lower guard (e = 0) gives zeros and subnormals, the upper guard (e = all-1) gives Inf with an empty tail and NaN with a non-empty one. A finite value is always (−1)^S · m · 2^E, and the grid densifies at zero and rarifies at large |x| — precisely because E is a power-of-two exponent, not a decimal one. Cheat sheet of special records: value S characteristic f hex +0 0 00000000 000…0 00000000 −0 1 00000000 000…0 80000000 +∞ 0 11111111 000…0 7F800000 −∞ 1 11111111 000…0 FF800000 typical qNaN 0 11111111 100…0 7FC00000 min. subnorm. > 0 0 00000000 000…1 00000001 max. finite 0 11111110 111…1 7F7FFFFF Didactic elaboration based on IEEE 754-1985 / 754-2008 / 754-2019 (binary interchange formats). Field names: S — sign, e — biased exponent, f — trailing significand. In Polish literature “cecha” = E or e (worth specifying whether with bias); “mantysa” = m = 1.f or 0.f. ================================================================================================ ================================================================================================ #13_specyfikacje_operacji_fpu.txt ================================================================================================ DIDACTIC NOTE · FPU ARITHMETIC IEEE 754 operations on the FPU What the floating-point unit really does with bits (alignment shift, adder, multiplier, normalizer, rounder), how mul differs from fused mul-add, which operations the standard requires, and which remain in the mathematical library and have no IEEE guarantee. 1. Three layers which must not be mixed The question “what the FPU can do on IEEE” falls into three different lists. Mixing them is the most common source of erroneous conclusions about accuracy. Layer What it is Guarantee of the result Standard IEEE 754-2019, clauses 5–6 Required operations: +, −, ×, ÷, sqrt, FMA, remainder, conversions, comparisons, min/max, nextUp, scaleB… Result uniquely determined: exactly one rounded to the target format, plus defined exceptions. FPU datapath Hardware blocks: unpack, shifter, multiplier tree, adder, LZA, rounder, pack. Sometimes also SRT or Newton iteration. This is implementation. The user does not see the shift. He sees an instruction which is to fulfill the IEEE contract. libm / language log, exp, pow, sin, hypot, rsqrt and hundreds of C/C++ functions. IEEE only recommends them (clause 9), does not require them. Result depends on the library, version and CPU. Error is sometimes > 1 ulp. This is not an “FPU operation outright”. Constructional conclusion: the FPU “outright” can do a few operations on the IEEE format. The entire rest of numerical analysis (logarithm, power, 1/x as a library function, trigonometry) is software composed of these few operations, often with tables and polynomials. 2. Datapath: what really sits in the FPU A number in a register is packed fields S | e | f. Before the adder or multiplier does anything, the circuit unpacks the word: it prepends the hidden bit (or 0 for a subnormal), separates the sign, converts the characteristic with bias to a working exponent. At the end it packs back and rounds. This is the common frame of every finite operation. Blocks, not “shift instructions” Block What it does on bits When it works Unpack / classify Extracts S, e, f. Prepends the hidden bit. Detects 0, Inf, NaN, subnormal. Every operation, before arithmetic starts. Alignment shifter Shifts the significand right by |E1 − E2|. Bits which fall out are folded into sticky. Addition and FMA. Usually not at multiplication. Multiplier array Product of full width: p × p bits (24×24 or 53×53, often Booth radix-4/16) → sum and carry. MUL and FMA. Sometimes also iterative DIV/SQRT. Adder (CPA / CSA) Adds or subtracts aligned significands. Far path (little cancellation) and near path (cancellation of leading ones). ADD/SUB and the second stage of FMA. LZA + normalizer Counts leading zeros and shifts left, to restore the form 1.f. This is the second shift. After subtraction of close numbers and after multiplication (shift by 0 or 1). Rounder (GRS) Guard, round, sticky decide whether to add 1 ulp. Mode: RNE, RTZ, RTP, RTN, RNT. Every numerical operation required by IEEE. Pack + exceptions Composes S | e | f, bias, special codes. Sets inexact, overflow, underflow, invalid, div-by-zero. Output of every instruction. Bit shifts — internal, not a user instruction IEEE has no operation “shift float by k bits”. The shift is a tool of the datapath, because one is allowed to add only significands with the same exponent. Alignment to the right. The smaller significand is shifted by the difference of characteristics. For float a difference > 25 bits (23 + GRS) means that the smaller operand disappears into sticky: it influences only the rounding, not the bits of the result. Hence a + b == a, when |b| is sufficiently small relative to |a|. Normalization to the left. After subtraction of close numbers the leading ones cancel. LZA (leading-zero anticipator) predicts by how much to shift, in parallel with the adder, so as not to wait for the result. Each bit of left shift is −1 to the exponent. If the exponent goes below E_min, the result becomes subnormal or zero (underflow). The only “shifting” operation visible to the programmer is scaleB / ldexp / scalbn: addition of an integer to the exponent. This is not a shifter of the mantissa, only arithmetic on the field e, with handling of overflow and underflow. frexp and logb are the inverse operation: extraction of the exponent. On many CPUs scalbn is an instruction, but semantically it is still IEEE, not a raw shift. What not to confuse with the FPU Casting bits float → uint32 and shifting on the integer ALU (classic Quake tricks, extraction of the characteristic by the mask 0x7F800000) is not an FPU operation. This is reinterpretation of memory. The result is not subject to IEEE rounding and breaks on subnormals, Inf and NaN, if the algorithm does not take this into account. 3. Adder: addition and subtraction Addition and subtraction share the same block. Subtraction is addition with the sign of the second operand inverted. IEEE contract: add(x, y) = round(x + y) exactly one rounding operation Steps on normal numbers: 1. Compare exponents. The larger remains the working exponent. 2. Shift the smaller significand right by the difference. Bits falling out fold into sticky (OR of all). 3. Same sign: add. Opposite signs: subtract. Near path (difference of exponents 0 or 1) must be able to cancel many bits; far path rounds, but normalizes by at most 1. 4. Normalize, round according to GRS, correct the exponent, pack. Consequence for the result: the error of adding two finite numbers which do not give overflow/underflow is at most ½ ulp of the result (round-ties-to-even mode). This does not mean that (a + b) + c = a + (b + c). Each addition rounds separately, and addition is not associative in IEEE. Special cases the adder handles beside, not in the adder: Inf + finite = Inf; Inf + (−Inf) = NaN (invalid); NaN infects; +0 + (−0) depends on the rounding mode (in RNE gives +0). 4. Multiplier Multiplication is simpler than addition, because there is no alignment of two independent exponents. Contract: mul(x, y) = round(x · y) On bits: sign = XOR of signs; working exponent = E1 + E2; significand = product (1.f1) × (1.f2), i.e. a number from the interval [1, 4). If the product ≥ 2, the normalizer shifts by 1 to the right and adds 1 to the exponent. Then one rounding to p bits. The width of the product is full: for double 53 × 53 = 106 bits before rounding. Therefore the multiplier itself “knows” the exact product. Rounding cuts it to 53. This distinction is the core of the difference mul vs FMA. Special: 0 · Inf = NaN (invalid); Inf · finite ≠ 0 = Inf; the sign of zero at multiplication is XOR, so (−2) · (−0) = +0. 5. mul versus fused mul-add — the difference which changes the result IEEE 754-2008 introduced fusedMultiplyAdd as a required operation. The definition is sharp: fma(x, y, z) = round( (x · y) + z ) where the product x · y is exact (infinite precision, in practice double width), the addition of z is also exact, and only the whole is rounded once to the target format. Two paths mul, then add FMA Roundings two: round(x · y), then round(this + z) one: round(x · y + z) Intermediate product truncated to p bits, error up to ½ ulp of the product held at full width (≈ 2p bits) Hardware separate multiplier, result returns to register, then adder one pipeline: multiplier tree → alignment of addend → adder → normalizer → rounder Inexact exception may fire twice (or once on mul, once on add) at most once, on the final result Contract two IEEE operations one IEEE operation, required since 754-2008 Numerical example in which the results diverge Take binary64: a = 1 + 2^{−52}, b = 1 − 2^{−52} Exact product: a · b = 1 − 2^{−104}. Half ulp at 1.0 is 2^{−53}. The term 2^{−104} is far below the rounding threshold, so mul(a, b) = 1.0 exactly, hex 0x1.0000000000000p+0 mul(a, b) − 1 = 0 FMA does not round the product. It adds −1 to the exact 1 − 2^{−104} and only this result is packed: fma(a, b, −1) = −2^{−104} = −0x1.0000000000000p−104 This is not an “error of FMA” nor an “error of mul”. These are two different contracts. Mul+add lost a bit which was smaller than ½ ulp of the product. FMA preserved it, because it rounded only the difference, whose scale is 2^{−104}. Second, practical consequence: FMA serves to recover the multiplication error. For finite a, b without overflow/underflow e = fma(a, b, −mul(a, b)) is the exact rounding error of the product (TwoProd / Dekker algorithm). Without FMA this error must be simulated by splitting the mantissa into halves (Veltkamp), which is slower and easier to spoil on subnormals. When FMA worsens, not improves FMA is more accurate for an expression which mathematically is one fma. A compiler which itself folds a*b+c into FMA (contraction, in C pragma STDC FP_CONTRACT, by default often ON in GCC/Clang at -O2 and -ffp-contract=on / fast), changes the result of the program relative to two separate operations. This is compliant with IEEE only if the language allows it. Consequences: Different value, sometimes closer to mathematics, sometimes breaking an identity which the code expected (e.g. test a*b+c == a*b+c written as two instructions). Different exception flags. In the example above mul+sub sets inexact, FMA of the result −2^{−104} is also inexact, but there are reverse cases: FMA can be exact where mul already is not. Different repeatability between CPU without FMA (old x87/SSE2) and CPU with FMA (Intel from Haswell, AMD from Piledriver / later full FMA3, ARMv8). Influence on the result: mul then add: two projections onto the format grid. FMA: one projection, but from a product of double precision. The difference is sometimes 0, sometimes 1 ulp, and at cancellation (subtraction of almost equal magnitudes) is many orders — exactly then FMA is what it was introduced for (scalar product, Horner, remainder of multiplication). procedure mul and difference with FMA mul(x, y) = round(x · y) fma(x, y, z) = round(x · y + z) FMA holds the product at full width and rounds once, after adding z. Mul, then add, rounds twice. For binary64, a = 1 + 2^{−52}, b = 1 − 2^{−52}: * exact product is 1 − 2^{−104} * mul(a, b) = 1, because 2^{−104} is far below ½ ulp at 1 * mul(a, b) − 1 = 0 * fma(a, b, −1) = −2^{−104} This is not an error of either path, only two contracts. At cancellation the difference is many orders, not 1 ulp. Hence the use of FMA in the scalar product and in recovery of the multiplication error: e = fma(a, b, −mul(a, b)). Compiler contraction (FP_CONTRACT) is allowed to fold a*b+c into FMA. The result and the inexact flag can change without change of the source. 6. Division and square root — required, but rarely “outright” IEEE requires correctly rounded division and square root. This does not mean that in silicon there is a table of dividers analogous to the multiplier. Two schools: SRT (digit recurrence) Iteration chooses successive digits of the quotient (radix 2, 4, 16) on the basis of the remainder. Each step is a short adder and choice of digit, not a full multiplier. The result at the end is rounded once. This is how the classic x87 divider and many ARM FPUs work. Newton / Goldschmidt iteration on the multiplier Hardware gives an approximation of the reciprocal (a few bits from a table), then 2–4 multiplications raise the precision to p+2 bits, at the end one rounding to IEEE. The user sees the instruction DIVSD, does not see the iteration. The result is to be the same as from SRT: round(x/y). sqrt is twin: either SRT, or Newton on 1/sqrt, then one multiplication by x. Contract: round(sqrt(x)), invalid for x < 0, +0 for +0, +Inf for +Inf. 1/x is not a separate IEEE operation. This is division 1 ÷ x, so it is required and correctly rounded, if you write it as div. A separate instruction RCP, which returns an approximation, is already not IEEE — see §8. 7. Operations required by IEEE, which the FPU usually has The list below is the contract of the standard (754-2019, clauses 5.3–5.7 and 5.3.3/5.10 after corrections of min/max), not a list of mnemonics. x86 names I give only as an anchor. IEEE operation Rounding Hardware note addition, subtraction yes, one ADDSS/ADDSD, SUB; common adder multiplication yes, one MULSS/MULSD; product tree division yes, one DIVSS/DIVSD; SRT or Newton squareRoot yes, one SQRTSS/SQRTSD fusedMultiplyAdd yes, one VFMADD*; required since 754-2008 remainder to nearest even quotient IEEE remainder, not C fmod roundToIntegral according to the mode ROUNDSS; truncation to integer in the same format conversions int ↔ float, float ↔ float yes CVTSS2SI, CVTSD2SS, … compare (quiet / signaling) no CMPSS; quiet does not signal NaN, signaling does minimum / maximum (+ magnitude) no SSE MAXPS is not the same: different NaN copy, negate, abs, copySign no sign bit; ANDPS/XORPS or dedicated scaleB, logB scaleB may round at underflow addition / subtraction of the characteristic nextUp, nextDown step of 1 ulp often microprogram or libm classification: isNaN, isInf, isNormal… no test of fields e and f, not arithmetic negate and abs do not touch the characteristic or mantissa: XOR or AND of bit 31 (float) / 63 (double). Therefore abs(NaN) remains NaN, and −0 changes into +0. copySign copies only the sign bit. These are the only FPU operations which really are purely bit-wise. IEEE remainder is x − n · y, where n = round(x/y) ties-to-even. C’s fmod rounds the quotient toward zero. This is not the same: remainder(5, 3) = −1, fmod(5, 3) = 2. On the FPU x87 there was the instruction FPREM/FPREM1; in SSE this is often realized by the library with DIV and FMA. min/max: 754-2008 had minNum/maxNum (NaN dies if the second argument is a number). 754-2019 recognized this as a defect and introduced minimum/maximum (NaN infects) and minimumNumber/maximumNumber. SSE MAXSS returns the second operand when either is NaN — none of these contracts. Relying on MAXSS as on IEEE max is an error. 8. Hardware instructions which are not in the standard Part of the FPU has approximate instructions. They are not IEEE operations, even if they stand next to DIV and SQRT. Instruction (x86) What it returns Status toward IEEE RCPSS / RCPPS approximation of 1/x, relative error typically ≤ 1.5 · 2^{−12} is not round(1/x). Serves as start of Newton. RSQRTSS / RSQRTPS approximation of 1/sqrt(x), similar accuracy is not rSqrt from clause 9. That is to be correctly rounded. FTZ / DAZ (MXCSR) subnormal on output → 0; subnormal on input → 0 breaks gradual underflow. Fast, non-compliant with IEEE. x87 80-bit extended 64-bit mantissa, 15-bit characteristic, without hidden bit format allowed as extended, but excess precision spoils repeatability, if you do not flush to 64/32. Newton on RCP: x1 = rcp(a); x2 = x1 · (2 − a · x1) doubles the number of good bits per step. Two–three steps give a full float, but the last step must be done so that the result is compliant with round(1/a) — bare RCP+Newton without correction of the last bit does not guarantee this. Therefore fast 1/x in games and correct 1/x in IEEE are different procedures. 9. Outside the standard: library, not FPU Clause 9 of IEEE 754-2019 recommends functions which the language should define. None is required for compliance with the standard. If an implementation supplies them as recommended operations, 754-2019 orders them to be rounded correctly. The C library does not promise this: libm is an implementation of the language, not an IEEE certificate. Recommended, not required (clause 9.2) Group IEEE functions C counterpart (approximate) exponentials exp, expm1, exp2, exp2m1, exp10, exp10m1 exp, expm1, exp2; exp10 usually absent logarithms log, log2, log10, logp1, log2p1, log10p1 log, log2, log10, log1p powers pow, powr, pown, rootn, compound pow, powf; the rest often missing inverse square root rSqrt = 1/sqrt(x) absent in C; present in GPU as approx hypotenuse hypot(x, y) hypot circular sin, cos, tan, asin, acos, atan, atan2 the same names in libm multiples of π sinPi, cosPi, tanPi, atanPi sinpi in POSIX, not in ISO C hyperbolic sinh, cosh, tanh, asinh, acosh, atanh the same names in libm pow, powr and pown are not interchangeable. pown(x, n) takes an integer exponent. pow(x, y) agrees with pown when y is integer, and is defined for partial negative x. powr(x, y) is defined as exp(y · log(x)), so x < 0 is outside the domain. C’s pow mixes these rules and historically had different results for (−1)^∞ and 0^0 depending on libm. What is library, even if it “looks like hardware” Function Why it is not an FPU operation outright log, log2, log10, log1p Argument reduction + minimax polynomial or table. Several–dozen mul/add/FMA. libm error usually < 1 ulp, not always ½ ulp. exp, exp2, expm1 The same: reduction to a small range, Horner on FMA. Overflow to Inf is required only if the library promises IEEE. pow(x, y) The hardest in libm. Integer path (pown) separate from exp(y · log(x)). Errors > 1 ulp occurred in historical libm. sin, cos, tan Reduction modulo π — and π is not exact in binary64. Quality of reduction dominates over the polynomial. GPU often has its own, weaker reduction. hypot sqrt(x² + y²) naively overflows. Correct hypot scales exponents. It is in C99, recommended in IEEE, rarely an instruction. 1/x as a function If it is DIV: required, ½ ulp. If RCPSS or intrinsic approx: outside the standard. rsqrt IEEE rSqrt is to be correctly rounded and is only recommended. Hardware RSQRTPS has ~12 bits and is not this. fmod C, not IEEE. Different rounding of the quotient than remainder. lgamma, erf, tgamma, Bessel Pure libm. IEEE standard does not list them. Boundary worth remembering Required and usually in silicon: +, −, ×, ÷, sqrt, FMA, comparisons, conversions, abs/neg. Required, often in microprogram or libm: remainder, nextafter, scaleB. Recommended, almost always libm, without guarantee of ½ ulp in C: log, exp, pow, sin and family. Completely outside the standard: approximate RCP/RSQRT, FTZ/DAZ, special functions. 10. What changes the result, even with the same formula The same mathematical formula gives different bits, when any of the following changes. This is a practical list of “influence on the result”. Factor Consequence Number of roundings mul+add ≠ FMA. Three operations ≠ one. Associativity does not hold. Contraction FP_CONTRACT Compiler is allowed to fold a*b+c into FMA. Result and flags can change without change of the source. Rounding mode RNE (default), RTZ, RTP, RTN. Changes the bit at ties and the direction of the error. Intermediate format x87 80-bit in registers vs SSE 64-bit. Excess precision masks an error which in binary64 is visible. FTZ / DAZ Subnormals disappear. The algorithm of gradual underflow stops working. Order of summation Sum of n numbers depends on the order. IEEE vector reductions outright say that the result may differ between implementations. NaN payload and min/max SSE max ≠ IEEE maximum. Propagation of NaN is not the same everywhere. Version of libm log/pow/sin are not determined to the bit. Different glibc, different MSVCRT, different GPU — different bits. 11. Cheat sheet Adder: align exponents (shift right + sticky), add significands, normalize (shift left), round once. Multiplier: XOR signs, add exponents, multiply 1.f × 1.f at full width, shift by at most 1, round once. FMA: the same product of full width, add the third operand before you cut anything, round once. Therefore fma(a, b, −1) can be non-zero where a*b − 1 is zero. Div and sqrt: required, correctly rounded, in silicon often iteration. 1/x as div is required; 1/x as RCP is not. Bit shift of float as a user instruction does not exist. There are only internal shifters and scaleB on the characteristic. log, exp, pow, sin, hypot, correct rsqrt: recommended by IEEE, not required, in practice libm. Approximate rsqrt/rcp: neither required nor recommended. One sentence The FPU outright realizes a few IEEE operations on the fields S, e, f: addition (shifter + adder), multiplication (multiplier), their gluing without intermediate rounding (FMA), and division and square root reduced to these blocks. Everything which is a transcendental function is a library composed of these blocks and does not inherit the guarantee of ½ ulp, until the implementation outright promises it. Didactic elaboration according to IEEE 754-2019 (ISO/IEC 60559:2020): required operations in clauses 5–6, recommended operations in clause 9. FMA defined as round(x · y + z) with one rounding. Example a = 1 + 2^{−52}, b = 1 − 2^{−52}: mul gives 1, fma(a, b, −1) gives −2^{−104}. x86 instruction names are illustration, not part of the standard. ================================================================================================ ================================================================================================ #List_of_sources ================================================================================================ The didactic notes are compilations prepared by language models according to prompts constructed on entropment.com for the purpose of listing for me what I need to know in order to read with understanding what is happening in the operations. They have been read and edited by (mainly by extracting cheat sheets and indicating essential cases). Numerical analyses (stacks of listed numbers in the algorithm itself) have not been attached, they are an element of loops available in the open code of the application, but not connected in the final application to the interface (these are test functions of selected loops). These are hundreds of megabytes of the course of data trajectories. The application has inside tools generating full analytical reports (Kolmogorov, Pearson) of a selected number of runs for selected input parameters, they are available from the IDE level, but are not hooked in the application to the interface (they would not be purposeful). The analysis of the code and translation into pseudocode comes from company data (which I happened to prepare for entropment.com) and was performed with support of models for translating code, whereas the indications of what in the logic should be taken into account and with what it is related between loops in the composition I performed with alert support of the company, because AI models defectively assign a domain for which there is no literature (the hybrid discussed in the text) and conclusions must be drawn independently. The source code and the application under the working name RIPA32 is available on the site https://entropment.com/releases/ it is in a language for prototyping and as such is not common, but very convenient structurally for a human; The source elaboration for implementation in the company is available (but the level of abstraction used there is too high for me): https://entropment.com/media/downloads/PAPERS_notes_for_QCO_in_order.txt ================================================================================================ ==============End of the list of appendices=======================