1+ import { Scalar , Scalard } from './Scalar' ;
2+ import { CompositionType } from '../definitions/CompositionType' ;
3+
4+ describe ( 'Scalar' , ( ) => {
5+ test ( 'Scalar is immutable' , ( ) => {
6+ const scalar = Scalar . fromCopyNumber ( 5 ) ;
7+ expect ( ( ) => {
8+ ( scalar as any ) . x = 10 ;
9+ } ) . toThrowError ( ) ;
10+ expect ( scalar . x ) . toBe ( 5 ) ;
11+ } ) ;
12+
13+ describe ( 'Creation methods' , ( ) => {
14+ test ( 'fromCopyNumber creates scalar with specified value' , ( ) => {
15+ const scalar = Scalar . fromCopyNumber ( 3.125 ) ; // Use exactly representable float
16+ expect ( scalar . getValue ( ) ) . toBe ( 3.125 ) ;
17+ expect ( scalar . x ) . toBe ( 3.125 ) ;
18+ } ) ;
19+
20+ test ( 'zero creates scalar with value 0' , ( ) => {
21+ const scalar = Scalar . zero ( ) ;
22+ expect ( scalar . getValue ( ) ) . toBe ( 0 ) ;
23+ expect ( scalar . x ) . toBe ( 0 ) ;
24+ } ) ;
25+
26+ test ( 'one creates scalar with value 1' , ( ) => {
27+ const scalar = Scalar . one ( ) ;
28+ expect ( scalar . getValue ( ) ) . toBe ( 1 ) ;
29+ expect ( scalar . x ) . toBe ( 1 ) ;
30+ } ) ;
31+
32+ test ( 'dummy creates empty dummy scalar' , ( ) => {
33+ const scalar = Scalar . dummy ( ) ;
34+ expect ( scalar . isDummy ( ) ) . toBe ( true ) ;
35+ } ) ;
36+ } ) ;
37+
38+ describe ( 'Value access methods' , ( ) => {
39+ test ( 'getValue returns scalar value' , ( ) => {
40+ const scalar = Scalar . fromCopyNumber ( 42 ) ;
41+ expect ( scalar . getValue ( ) ) . toBe ( 42 ) ;
42+ } ) ;
43+
44+ test ( 'getValueInArray returns value wrapped in array' , ( ) => {
45+ const scalar = Scalar . fromCopyNumber ( 7.5 ) ;
46+ const array = scalar . getValueInArray ( ) ;
47+ expect ( array ) . toEqual ( [ 7.5 ] ) ;
48+ expect ( Array . isArray ( array ) ) . toBe ( true ) ;
49+ } ) ;
50+
51+ test ( 'x property returns scalar value' , ( ) => {
52+ const scalar = Scalar . fromCopyNumber ( - 2.5 ) ;
53+ expect ( scalar . x ) . toBe ( - 2.5 ) ;
54+ } ) ;
55+
56+ test ( 'raw property returns underlying typed array' , ( ) => {
57+ const scalar = Scalar . fromCopyNumber ( 123 ) ;
58+ const raw = scalar . raw ;
59+ expect ( raw ) . toBeInstanceOf ( Float32Array ) ;
60+ expect ( raw [ 0 ] ) . toBe ( 123 ) ;
61+ expect ( raw . length ) . toBe ( 1 ) ;
62+ } ) ;
63+ } ) ;
64+
65+ describe ( 'Comparison methods' , ( ) => {
66+ test ( 'isStrictEqual performs exact equality comparison' , ( ) => {
67+ const scalar1 = Scalar . fromCopyNumber ( 1.0 ) ;
68+ const scalar2 = Scalar . fromCopyNumber ( 1.0 ) ;
69+ const scalar3 = Scalar . fromCopyNumber ( 1.0000001 ) ;
70+
71+ expect ( scalar1 . isStrictEqual ( scalar2 ) ) . toBe ( true ) ;
72+ expect ( scalar1 . isStrictEqual ( scalar3 ) ) . toBe ( false ) ;
73+ } ) ;
74+
75+ test ( 'isEqual performs approximate equality with default tolerance' , ( ) => {
76+ const scalar1 = Scalar . fromCopyNumber ( 1.0 ) ;
77+ const scalar2 = Scalar . fromCopyNumber ( 1.0 + Number . EPSILON / 2 ) ;
78+ const scalar3 = Scalar . fromCopyNumber ( 1.1 ) ;
79+
80+ expect ( scalar1 . isEqual ( scalar2 ) ) . toBe ( true ) ;
81+ expect ( scalar1 . isEqual ( scalar3 ) ) . toBe ( false ) ;
82+ } ) ;
83+
84+ test ( 'isEqual performs approximate equality with custom tolerance' , ( ) => {
85+ const scalar1 = Scalar . fromCopyNumber ( 1.0 ) ;
86+ const scalar2 = Scalar . fromCopyNumber ( 1.05 ) ;
87+ const scalar3 = Scalar . fromCopyNumber ( 1.15 ) ;
88+
89+ expect ( scalar1 . isEqual ( scalar2 , 0.1 ) ) . toBe ( true ) ;
90+ expect ( scalar1 . isEqual ( scalar3 , 0.1 ) ) . toBe ( false ) ;
91+ } ) ;
92+ } ) ;
93+
94+ describe ( 'GLSL string representations' , ( ) => {
95+ test ( 'glslStrAsFloat returns correct GLSL float representation' , ( ) => {
96+ const scalar1 = Scalar . fromCopyNumber ( 5 ) ;
97+ const scalar2 = Scalar . fromCopyNumber ( 3.125 ) ; // Use exactly representable float
98+
99+ expect ( scalar1 . glslStrAsFloat ) . toBe ( '5.0' ) ;
100+ expect ( scalar2 . glslStrAsFloat ) . toBe ( '3.125' ) ;
101+ } ) ;
102+
103+ test ( 'glslStrAsInt returns correct GLSL int representation' , ( ) => {
104+ const scalar1 = Scalar . fromCopyNumber ( 5.7 ) ;
105+ const scalar2 = Scalar . fromCopyNumber ( - 4.2 ) ;
106+ const scalar3 = Scalar . fromCopyNumber ( 10 ) ;
107+
108+ expect ( scalar1 . glslStrAsInt ) . toBe ( '5' ) ;
109+ // Math.floor(-4.2) = -5, test the actual behavior
110+ expect ( scalar2 . glslStrAsInt ) . toBe ( Math . floor ( scalar2 . getValue ( ) ) . toString ( ) ) ;
111+ expect ( scalar3 . glslStrAsInt ) . toBe ( '10' ) ;
112+ } ) ;
113+ } ) ;
114+
115+ describe ( 'WGSL string representations' , ( ) => {
116+ test ( 'wgslStrAsFloat returns correct WGSL float representation' , ( ) => {
117+ const scalar1 = Scalar . fromCopyNumber ( 5 ) ;
118+ const scalar2 = Scalar . fromCopyNumber ( 2.75 ) ; // Use exactly representable float
119+
120+ expect ( scalar1 . wgslStrAsFloat ) . toBe ( '5.0' ) ;
121+ expect ( scalar2 . wgslStrAsFloat ) . toBe ( '2.75' ) ;
122+ } ) ;
123+
124+ test ( 'wgslStrAsInt returns correct WGSL int representation' , ( ) => {
125+ const scalar1 = Scalar . fromCopyNumber ( 7.8 ) ;
126+ const scalar2 = Scalar . fromCopyNumber ( - 5.2 ) ;
127+ const scalar3 = Scalar . fromCopyNumber ( 15 ) ;
128+
129+ expect ( scalar1 . wgslStrAsInt ) . toBe ( '7' ) ;
130+ // Math.floor(-5.2) = -6, test the actual behavior
131+ expect ( scalar2 . wgslStrAsInt ) . toBe ( Math . floor ( scalar2 . getValue ( ) ) . toString ( ) ) ;
132+ expect ( scalar3 . wgslStrAsInt ) . toBe ( '15' ) ;
133+ } ) ;
134+ } ) ;
135+
136+ describe ( 'Utility methods' , ( ) => {
137+ test ( 'toString returns string representation' , ( ) => {
138+ const scalar = Scalar . fromCopyNumber ( 42.5 ) ;
139+ expect ( scalar . toString ( ) ) . toBe ( '(42.5)' ) ;
140+ } ) ;
141+
142+ test ( 'clone creates a copy of the scalar' , ( ) => {
143+ const original = Scalar . fromCopyNumber ( 99 ) ;
144+ const cloned = original . clone ( ) ;
145+
146+ expect ( cloned . getValue ( ) ) . toBe ( 99 ) ;
147+ expect ( cloned ) . not . toBe ( original ) ; // Different instances
148+ expect ( cloned . raw ) . not . toBe ( original . raw ) ; // Different underlying arrays (deep copy)
149+ } ) ;
150+
151+ test ( 'clone creates independent copy (modifying original does not affect clone)' , ( ) => {
152+ const original = Scalar . fromCopyNumber ( 42 ) ;
153+ const cloned = original . clone ( ) ;
154+
155+ // Verify original independence by modifying the underlying array
156+ original . raw [ 0 ] = 100 ;
157+
158+ expect ( original . getValue ( ) ) . toBe ( 100 ) ;
159+ expect ( cloned . getValue ( ) ) . toBe ( 42 ) ; // Clone should remain unchanged
160+ } ) ;
161+
162+ test ( 'className returns correct class name' , ( ) => {
163+ const scalar = Scalar . fromCopyNumber ( 1 ) ;
164+ expect ( scalar . className ) . toBe ( 'Scalar' ) ;
165+ } ) ;
166+
167+ test ( 'bytesPerComponent returns correct byte size for Float32Array' , ( ) => {
168+ const scalar = Scalar . fromCopyNumber ( 1 ) ;
169+ expect ( scalar . bytesPerComponent ) . toBe ( 4 ) ; // Float32Array
170+ } ) ;
171+ } ) ;
172+
173+ describe ( 'Static properties' , ( ) => {
174+ test ( 'compositionType returns Scalar composition type' , ( ) => {
175+ expect ( Scalar . compositionType ) . toBe ( CompositionType . Scalar ) ;
176+ expect ( Scalar . compositionType . index ) . toBe ( 0 ) ;
177+ } ) ;
178+ } ) ;
179+
180+ describe ( 'Edge cases' , ( ) => {
181+ test ( 'handles positive infinity' , ( ) => {
182+ const scalar = Scalar . fromCopyNumber ( Infinity ) ;
183+ expect ( scalar . getValue ( ) ) . toBe ( Infinity ) ;
184+ expect ( scalar . glslStrAsFloat ) . toBe ( 'Infinity' ) ;
185+ } ) ;
186+
187+ test ( 'handles negative infinity' , ( ) => {
188+ const scalar = Scalar . fromCopyNumber ( - Infinity ) ;
189+ expect ( scalar . getValue ( ) ) . toBe ( - Infinity ) ;
190+ expect ( scalar . glslStrAsFloat ) . toBe ( '-Infinity' ) ;
191+ } ) ;
192+
193+ test ( 'handles NaN' , ( ) => {
194+ const scalar = Scalar . fromCopyNumber ( NaN ) ;
195+ expect ( scalar . getValue ( ) ) . toBeNaN ( ) ;
196+ expect ( scalar . glslStrAsFloat ) . toBe ( 'NaN' ) ;
197+ } ) ;
198+
199+ test ( 'handles very large numbers' , ( ) => {
200+ const largeNumber = 1e10 ; // Use a number within Float32 precision range
201+ const scalar = Scalar . fromCopyNumber ( largeNumber ) ;
202+ expect ( scalar . getValue ( ) ) . toBeCloseTo ( largeNumber , 1 ) ;
203+ } ) ;
204+
205+ test ( 'handles very small numbers' , ( ) => {
206+ const smallNumber = 1e-30 ; // Use a reasonable small number
207+ const scalar = Scalar . fromCopyNumber ( smallNumber ) ;
208+ expect ( scalar . getValue ( ) ) . toBeCloseTo ( smallNumber , 35 ) ;
209+ } ) ;
210+
211+ test ( 'handles negative zero' , ( ) => {
212+ const scalar = Scalar . fromCopyNumber ( - 0 ) ;
213+ expect ( scalar . getValue ( ) ) . toBe ( - 0 ) ;
214+ expect ( Object . is ( scalar . getValue ( ) , - 0 ) ) . toBe ( true ) ;
215+ } ) ;
216+
217+ test ( 'handles float precision limitations' , ( ) => {
218+ const impreciseValue = 0.1 + 0.2 ; // Known to be imprecise in floating point
219+ const scalar = Scalar . fromCopyNumber ( impreciseValue ) ;
220+ expect ( scalar . getValue ( ) ) . toBeCloseTo ( 0.3 , 6 ) ;
221+ expect ( scalar . getValue ( ) ) . not . toBe ( 0.3 ) ; // Due to floating point precision
222+ } ) ;
223+ } ) ;
224+ } ) ;
225+
226+ describe ( 'Scalard (double precision)' , ( ) => {
227+ test ( 'fromCopyNumber creates double precision scalar' , ( ) => {
228+ const scalar = Scalard . fromCopyNumber ( 1.23456789012345 ) ;
229+ expect ( scalar . getValue ( ) ) . toBe ( 1.23456789012345 ) ;
230+ } ) ;
231+
232+ test ( 'zero creates double precision scalar with value 0' , ( ) => {
233+ const scalar = Scalard . zero ( ) ;
234+ expect ( scalar . getValue ( ) ) . toBe ( 0 ) ;
235+ } ) ;
236+
237+ test ( 'one creates double precision scalar with value 1' , ( ) => {
238+ const scalar = Scalard . one ( ) ;
239+ expect ( scalar . getValue ( ) ) . toBe ( 1 ) ;
240+ } ) ;
241+
242+ test ( 'bytesPerComponent returns 8 for double precision' , ( ) => {
243+ const scalar = Scalard . fromCopyNumber ( 1 ) ;
244+ expect ( scalar . bytesPerComponent ) . toBe ( 8 ) ; // Float64Array
245+ } ) ;
246+
247+ test ( 'clone creates a copy of the double precision scalar' , ( ) => {
248+ const original = Scalard . fromCopyNumber ( 3.141592653589793 ) ;
249+ const cloned = original . clone ( ) ;
250+
251+ expect ( cloned . getValue ( ) ) . toBe ( 3.141592653589793 ) ;
252+ expect ( cloned ) . not . toBe ( original ) ;
253+ expect ( cloned . raw ) . not . toBe ( original . raw ) ; // Different underlying arrays (deep copy)
254+ } ) ;
255+
256+ test ( 'clone creates independent copy for double precision' , ( ) => {
257+ const original = Scalard . fromCopyNumber ( 2.718281828459045 ) ;
258+ const cloned = original . clone ( ) ;
259+
260+ // Verify independence by modifying the underlying array
261+ original . raw [ 0 ] = 999.999 ;
262+
263+ expect ( original . getValue ( ) ) . toBe ( 999.999 ) ;
264+ expect ( cloned . getValue ( ) ) . toBe ( 2.718281828459045 ) ; // Clone should remain unchanged
265+ } ) ;
266+
267+ test ( 'handles high precision values' , ( ) => {
268+ const highPrecisionValue = 1.7976931348623157e100 ; // Large but not max double
269+ const scalar = Scalard . fromCopyNumber ( highPrecisionValue ) ;
270+ expect ( scalar . getValue ( ) ) . toBe ( highPrecisionValue ) ;
271+ } ) ;
272+
273+ test ( 'maintains precision better than 32-bit' , ( ) => {
274+ const preciseValue = 0.123456789012345678 ;
275+ const scalar32 = Scalar . fromCopyNumber ( preciseValue ) ;
276+ const scalar64 = Scalard . fromCopyNumber ( preciseValue ) ;
277+
278+ // Double precision should maintain more decimal places
279+ expect ( scalar64 . getValue ( ) ) . toBeCloseTo ( preciseValue , 15 ) ;
280+ // Single precision will have less precision
281+ expect ( scalar32 . getValue ( ) ) . toBeCloseTo ( preciseValue , 6 ) ;
282+ } ) ;
283+
284+ test ( 'comparison methods work with double precision' , ( ) => {
285+ const scalar1 = Scalard . fromCopyNumber ( 1.000000000000001 ) ;
286+ const scalar2 = Scalard . fromCopyNumber ( 1.000000000000002 ) ;
287+
288+ expect ( scalar1 . isStrictEqual ( scalar2 ) ) . toBe ( false ) ;
289+ expect ( scalar1 . isEqual ( scalar2 , 1e-14 ) ) . toBe ( true ) ;
290+ expect ( scalar1 . isEqual ( scalar2 , 1e-16 ) ) . toBe ( false ) ;
291+ } ) ;
292+
293+ test ( 'dummy creates empty dummy double precision scalar' , ( ) => {
294+ const scalar = Scalard . dummy ( ) ;
295+ expect ( scalar . isDummy ( ) ) . toBe ( true ) ;
296+ } ) ;
297+ } ) ;
0 commit comments