package epoch import ( "testing" "git.smesh.lol/gnarl-hamadryad/ratio" ) func TestNew(t *testing.T) { tests := []struct { name string decExp int binExp int dec int64 bin int64 period int64 }{ {"Colony", 2, 8, 100, 256, 25600}, {"EngineDissolveLCM", 2, 4, 100, 16, 1600}, {"PureBinary", 0, 10, 1, 1024, 1024}, {"PureDecimal", 3, 0, 1000, 1, 1000}, {"Unity", 0, 0, 1, 1, 1}, {"CryptoWalk256", 3, 1, 1000, 2, 2000}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { e := New(tt.decExp, tt.binExp) if e.DecFactor != tt.dec { t.Errorf("DecFactor = %d, want %d", e.DecFactor, tt.dec) } if e.BinFactor != tt.bin { t.Errorf("BinFactor = %d, want %d", e.BinFactor, tt.bin) } if e.Period != tt.period { t.Errorf("Period = %d, want %d", e.Period, tt.period) } }) } } func TestNewPanics(t *testing.T) { tests := []struct { name string decExp int binExp int }{ {"NegativeDec", -1, 0}, {"NegativeBin", 0, -1}, {"BinOverflow", 0, 63}, {"DecOverflow", 19, 0}, // 10^19 > int64 max } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("expected panic for New(%d, %d)", tt.decExp, tt.binExp) } }() New(tt.decExp, tt.binExp) }) } } func TestNamedEpochs(t *testing.T) { tests := []struct { name string epoch Epoch period int64 }{ {"Colony", Colony, 25600}, {"EngineDissolveLCM", EngineDissolveLCM, 1600}, {"FitnessSampling", FitnessSampling, 25600}, {"CryptoWalk128", CryptoWalk128, 1024}, {"CryptoWalk192", CryptoWalk192, 1600}, {"CryptoWalk256", CryptoWalk256, 2000}, {"EWMACap", EWMACap, 1_000_000_000}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.epoch.Period != tt.period { t.Errorf("Period = %d, want %d", tt.epoch.Period, tt.period) } }) } } func TestOnBoundary(t *testing.T) { e := Colony // Period = 25600 tests := []struct { n int64 want bool }{ {0, false}, {1, false}, {100, false}, {256, false}, {25599, false}, {25600, true}, {51200, true}, {-25600, false}, } for _, tt := range tests { if got := e.OnBoundary(tt.n); got != tt.want { t.Errorf("Colony.OnBoundary(%d) = %v, want %v", tt.n, got, tt.want) } } } func TestOnDecimal(t *testing.T) { e := Colony // DecFactor = 100 tests := []struct { n int64 want bool }{ {0, false}, {99, false}, {100, true}, {200, true}, {256, false}, {25600, true}, } for _, tt := range tests { if got := e.OnDecimal(tt.n); got != tt.want { t.Errorf("Colony.OnDecimal(%d) = %v, want %v", tt.n, got, tt.want) } } } func TestOnBinary(t *testing.T) { e := Colony // BinFactor = 256 tests := []struct { n int64 want bool }{ {0, false}, {100, false}, {255, false}, {256, true}, {512, true}, {25600, true}, } for _, tt := range tests { if got := e.OnBinary(tt.n); got != tt.want { t.Errorf("Colony.OnBinary(%d) = %v, want %v", tt.n, got, tt.want) } } } func TestPhaseAtBoundary(t *testing.T) { e := Colony // At epoch boundaries, phase is zero. if p := e.Phase(0); !p.IsZero() { t.Errorf("Phase(0) = %s, want 0", p) } if p := e.Phase(25600); !p.IsZero() { t.Errorf("Phase(25600) = %s, want 0", p) } if p := e.Phase(51200); !p.IsZero() { t.Errorf("Phase(51200) = %s, want 0", p) } } func TestPhaseNonZero(t *testing.T) { e := Colony // At step 100: decimal clock at 0/100 (complete cycle), binary at 100/256. // Phase = 100/256 - 0/100 = 25/64. p := e.Phase(100) want := ratio.New(25, 64) if !p.Equal(want) { t.Errorf("Phase(100) = %s, want %s", p, want) } // At step 256: binary clock at 0/256 (complete cycle), decimal at 56/100. // Phase = 0/256 - 56/100 = -56/100 = -14/25. p = e.Phase(256) want = ratio.New(-14, 25) if !p.Equal(want) { t.Errorf("Phase(256) = %s, want %s", p, want) } } func TestBinaryStepsPerDecimal(t *testing.T) { // Colony: 256/100 = 64/25. r := Colony.BinaryStepsPerDecimal() want := ratio.New(64, 25) if !r.Equal(want) { t.Errorf("Colony.BinaryStepsPerDecimal() = %s, want %s", r, want) } } func TestDecimalStepsPerBinary(t *testing.T) { // Colony: 100/256 = 25/64. r := Colony.DecimalStepsPerBinary() want := ratio.New(25, 64) if !r.Equal(want) { t.Errorf("Colony.DecimalStepsPerBinary() = %s, want %s", r, want) } } func TestImagPhaseAtBoundary(t *testing.T) { e := Colony // At epoch boundaries, both clocks are at 0, so Im = 0*0 = 0. if p := e.ImagPhase(0); !p.IsZero() { t.Errorf("ImagPhase(0) = %s, want 0", p) } if p := e.ImagPhase(25600); !p.IsZero() { t.Errorf("ImagPhase(25600) = %s, want 0", p) } } func TestImagPhaseAtDecBoundary(t *testing.T) { e := Colony // BinFactor=256, DecFactor=100 // At step 100: binFrac=100/256=25/64, decFrac=0/100=0. Im=0. if p := e.ImagPhase(100); !p.IsZero() { t.Errorf("ImagPhase(100) = %s, want 0", p) } } func TestImagPhaseNonZero(t *testing.T) { e := Colony // BinFactor=256, DecFactor=100 // At step 50: binFrac=50/256=25/128, decFrac=50/100=1/2 // Im = 25/128 * 1/2 = 25/256 p := e.ImagPhase(50) want := ratio.New(25, 256) if !p.Equal(want) { t.Errorf("ImagPhase(50) = %s, want %s", p, want) } } func TestComplexPhase(t *testing.T) { e := Colony cp := e.ComplexPhase(50) if !cp.Re.Equal(e.Phase(50)) { t.Errorf("ComplexPhase.Re = %s, want %s", cp.Re, e.Phase(50)) } if !cp.Im.Equal(e.ImagPhase(50)) { t.Errorf("ComplexPhase.Im = %s, want %s", cp.Im, e.ImagPhase(50)) } } func TestString(t *testing.T) { tests := []struct { epoch Epoch want string }{ {Colony, "10^2×2^8=25600"}, {CryptoWalk128, "10^0×2^10=1024"}, {EWMACap, "10^9×2^0=1000000000"}, } for _, tt := range tests { if got := tt.epoch.String(); got != tt.want { t.Errorf("String() = %q, want %q", got, tt.want) } } }