Files
Miha Kralj 67ad6f0cba v0.8.7: Replace periodic ResyncInterval with Kahan compensated summation
Comprehensive refactor across all indicators replacing the periodic
ResyncInterval-based drift correction (every 1000 ticks recalculate
from scratch) with Kahan compensated summation for running sums.

Key changes:
- Remove ResyncInterval constants and TickCount fields from all State records
- Add Kahan compensation fields (SumComp, SumSqComp, etc.) to State records
- Replace naive sum += val - removed with Kahan delta pattern
- Remove Resync()/RecalculateSum() methods that did O(N) recalculation
- Update batch/SIMD paths to use Kahan compensation instead of resync loops
- IIR filters (EMA, REMA, RGMA) simplified: inherently self-correcting
- Version bump to 0.8.7
- Build system: README version stamping via Directory.Build.props
- Minor doc/test tolerance adjustments for new numerical characteristics

Affected modules: channels, core, cycles, dynamics, errors, momentum,
oscillators, statistics, trends_FIR, trends_IIR, volatility, volume
2026-03-13 22:01:31 -07:00

757 lines
26 KiB
C#

using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace QuanTAlib;
/// <summary>
/// Aberr: Aberration Bands
/// </summary>
/// <remarks>
/// Aberration Bands measure price deviation from a central moving average using absolute
/// deviation rather than standard deviation. This approach provides more intuitive and
/// outlier-resistant bands compared to Bollinger Bands.
///
/// Calculation:
/// Middle Band = SMA(Source, Period)
/// Deviation = |Source - Middle|
/// Average Deviation = SMA(Deviation, Period)
/// Upper Band = Middle + (Multiplier x Average Deviation)
/// Lower Band = Middle - (Multiplier x Average Deviation)
///
/// Key characteristics:
/// - Uses absolute deviation instead of standard deviation
/// - Less sensitive to extreme outliers than Bollinger Bands
/// - Provides intuitive measure of typical price dispersion
/// - Bands expand during volatile periods and contract during consolidation
///
/// Sources:
/// Pine Script implementation: https://github.com/mihakralj/pinescript/blob/main/indicators/channels/aberr.pine
/// </remarks>
[SkipLocalsInit]
public sealed class Aberr : ITValuePublisher, IDisposable
{
private readonly int _period;
private readonly double _multiplier;
private readonly RingBuffer _sourceBuffer;
private readonly RingBuffer _deviationBuffer;
private readonly TValuePublishedHandler _handler;
private ITValuePublisher? _source;
private bool _disposed;
[StructLayout(LayoutKind.Auto)]
private record struct State(
double SumSource,
double SumDeviation,
double SumSourceComp,
double SumDeviationComp,
double LastValidValue
);
private State _state;
private State _pState;
/// <summary>
/// Display name for the indicator.
/// </summary>
public string Name { get; }
/// <summary>
/// Number of periods before the indicator is considered "hot" (valid).
/// </summary>
public int WarmupPeriod { get; }
/// <summary>
/// Current middle band value (SMA of source).
/// </summary>
public TValue Last { get; private set; }
/// <summary>
/// Current upper band value.
/// </summary>
public TValue Upper { get; private set; }
/// <summary>
/// Current lower band value.
/// </summary>
public TValue Lower { get; private set; }
/// <summary>
/// True if the indicator has enough data to produce valid results.
/// </summary>
public bool IsHot => _sourceBuffer.IsFull;
/// <summary>
/// Event triggered when a new TValue is available.
/// </summary>
public event TValuePublishedHandler? Pub;
/// <summary>
/// Creates Aberr with specified period and multiplier.
/// </summary>
/// <param name="period">Lookback period for SMA and deviation calculations (must be > 0)</param>
/// <param name="multiplier">Multiplier for band width (must be > 0, default: 2.0)</param>
public Aberr(int period, double multiplier = 2.0)
{
if (period <= 0)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
}
if (multiplier <= 0)
{
throw new ArgumentOutOfRangeException(nameof(multiplier), "Multiplier must be greater than 0");
}
_period = period;
_multiplier = multiplier;
_sourceBuffer = new RingBuffer(period);
_deviationBuffer = new RingBuffer(period);
Name = $"Aberr({period},{multiplier:F2})";
WarmupPeriod = period;
_handler = HandleValue;
}
/// <summary>
/// Creates Aberr with TSeries source.
/// </summary>
public Aberr(TSeries source, int period, double multiplier = 2.0) : this(period, multiplier)
{
_source = source ?? throw new ArgumentNullException(nameof(source));
Prime(source);
_source.Pub += _handler;
}
/// <summary>
/// Creates Aberr with ITValuePublisher source.
/// </summary>
public Aberr(ITValuePublisher source, int period, double multiplier = 2.0) : this(period, multiplier)
{
_source = source ?? throw new ArgumentNullException(nameof(source));
_source.Pub += _handler;
}
private void HandleValue(object? _, in TValueEventArgs e) => Update(e.Value, e.IsNew);
/// <summary>
/// Helper to invoke the Pub event.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void PubEvent(TValue value, bool isNew)
{
Pub?.Invoke(this, new TValueEventArgs { Value = value, IsNew = isNew });
}
/// <summary>
/// Gets a valid input value, using last-value substitution for non-finite inputs.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private double GetValidValue(double input)
{
if (double.IsFinite(input))
{
_state.LastValidValue = input;
return input;
}
return _state.LastValidValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateState(double value, double deviation)
{
double removedSource = _sourceBuffer.Count == _sourceBuffer.Capacity ? _sourceBuffer.Oldest : 0.0;
double removedDeviation = _deviationBuffer.Count == _deviationBuffer.Capacity ? _deviationBuffer.Oldest : 0.0;
// Kahan compensated summation for SumSource
double srcDelta = value - removedSource - _state.SumSourceComp;
double srcNewSum = _state.SumSource + srcDelta;
_state.SumSourceComp = (srcNewSum - _state.SumSource) - srcDelta;
_state.SumSource = srcNewSum;
// Kahan compensated summation for SumDeviation
double devDelta = deviation - removedDeviation - _state.SumDeviationComp;
double devNewSum = _state.SumDeviation + devDelta;
_state.SumDeviationComp = (devNewSum - _state.SumDeviation) - devDelta;
_state.SumDeviation = devNewSum;
_sourceBuffer.Add(value);
_deviationBuffer.Add(deviation);
}
/// <summary>
/// Updates the indicator with a TValue input.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue Update(TValue input, bool isNew = true)
{
double value = GetValidValue(input.Value);
if (isNew)
{
_pState = _state;
// Compute SMA including the new value with correct divisor (matches batch ProcessMainLoop)
int count = _sourceBuffer.Count;
double removedSource = count == _sourceBuffer.Capacity ? _sourceBuffer.Oldest : 0.0;
double newSum = _state.SumSource - removedSource + value;
int newCount = count < _sourceBuffer.Capacity ? count + 1 : count;
double sma = newSum / newCount;
double deviation = Math.Abs(value - sma);
UpdateState(value, deviation);
}
else
{
_state = _pState;
// Replace newest source value and recompute sum for current-bar SMA (matches Pine)
_sourceBuffer.UpdateNewest(value);
double currentSum = _sourceBuffer.Sum;
int corrCount = _sourceBuffer.Count;
double corrSma = corrCount > 0 ? currentSum / corrCount : value;
double corrDeviation = Math.Abs(value - corrSma);
_deviationBuffer.UpdateNewest(corrDeviation);
_state = _state with
{
SumSource = currentSum,
SumDeviation = _deviationBuffer.Sum,
SumSourceComp = 0,
SumDeviationComp = 0,
};
}
int currentCount = _sourceBuffer.Count;
if (currentCount == 0)
{
Last = new TValue(input.Time, double.NaN);
Upper = new TValue(input.Time, double.NaN);
Lower = new TValue(input.Time, double.NaN);
}
else
{
double middle = _state.SumSource / currentCount;
double avgDeviation = _state.SumDeviation / currentCount;
double bandWidth = _multiplier * avgDeviation;
Last = new TValue(input.Time, middle);
Upper = new TValue(input.Time, middle + bandWidth);
Lower = new TValue(input.Time, middle - bandWidth);
}
PubEvent(Last, isNew);
return Last;
}
/// <summary>
/// Updates the indicator with a TSeries.
/// </summary>
public (TSeries Middle, TSeries Upper, TSeries Lower) Update(TSeries source)
{
if (source.Count == 0)
{
return (new TSeries([], []), new TSeries([], []), new TSeries([], []));
}
int len = source.Count;
var tMiddle = new List<long>(len);
var vMiddle = new List<double>(len);
var tUpper = new List<long>(len);
var vUpper = new List<double>(len);
var tLower = new List<long>(len);
var vLower = new List<double>(len);
CollectionsMarshal.SetCount(tMiddle, len);
CollectionsMarshal.SetCount(vMiddle, len);
CollectionsMarshal.SetCount(tUpper, len);
CollectionsMarshal.SetCount(vUpper, len);
CollectionsMarshal.SetCount(tLower, len);
CollectionsMarshal.SetCount(vLower, len);
var tSpan = CollectionsMarshal.AsSpan(tMiddle);
var vMiddleSpan = CollectionsMarshal.AsSpan(vMiddle);
var vUpperSpan = CollectionsMarshal.AsSpan(vUpper);
var vLowerSpan = CollectionsMarshal.AsSpan(vLower);
// Use batch calculation
Batch(source.Values, vMiddleSpan, vUpperSpan, vLowerSpan, _period, _multiplier);
source.Times.CopyTo(tSpan);
// Copy timestamps to upper and lower (same time series)
tSpan.CopyTo(CollectionsMarshal.AsSpan(tUpper));
tSpan.CopyTo(CollectionsMarshal.AsSpan(tLower));
// Prime the state for continued streaming
Prime(source);
return (new TSeries(tMiddle, vMiddle), new TSeries(tUpper, vUpper), new TSeries(tLower, vLower));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ResyncSums(int period, ref WorkBuffers buffers, ref ScalarState state)
{
state.TickCount = 0;
if (Vector.IsHardwareAccelerated && period >= Vector<double>.Count)
{
ReadOnlySpan<double> sourceSpan = buffers.Source;
ReadOnlySpan<double> deviationSpan = buffers.Deviation;
state.SumSource = sourceSpan.SumSIMD();
state.SumDeviation = deviationSpan.SumSIMD();
}
else
{
double recalcSumSource = 0;
double recalcSumDeviation = 0;
for (int k = 0; k < period; k++)
{
recalcSumSource += buffers.Source[k];
recalcSumDeviation += buffers.Deviation[k];
}
state.SumSource = recalcSumSource;
state.SumDeviation = recalcSumDeviation;
}
}
/// <summary>
/// Initializes the indicator state using the provided TSeries history.
/// </summary>
public void Prime(TSeries source)
{
if (source.Count == 0)
{
return;
}
// Reset state
_sourceBuffer.Clear();
_deviationBuffer.Clear();
_state = default;
_pState = default;
int warmupLength = Math.Min(source.Count, WarmupPeriod);
int startIndex = source.Count - warmupLength;
// Seed LastValidValue
_state.LastValidValue = double.NaN;
for (int i = startIndex - 1; i >= 0; i--)
{
if (double.IsFinite(source[i].Value))
{
_state.LastValidValue = source[i].Value;
break;
}
}
// Find valid value in warmup window if not found
if (double.IsNaN(_state.LastValidValue))
{
for (int i = startIndex; i < source.Count; i++)
{
if (double.IsFinite(source[i].Value))
{
_state.LastValidValue = source[i].Value;
break;
}
}
}
// Feed the buffers
for (int i = startIndex; i < source.Count; i++)
{
double value = GetValidValue(source[i].Value);
// Compute SMA including the new value with correct divisor (matches batch ProcessMainLoop)
int count = _sourceBuffer.Count;
double removedSource = count == _sourceBuffer.Capacity ? _sourceBuffer.Oldest : 0.0;
double newSum = _state.SumSource - removedSource + value;
int newCount = count < _sourceBuffer.Capacity ? count + 1 : count;
double sma = newSum / newCount;
double deviation = Math.Abs(value - sma);
UpdateState(value, deviation);
}
// Finalize state
int currentCount = _sourceBuffer.Count;
if (currentCount > 0)
{
var lastItem = source.Last;
double middle = _state.SumSource / currentCount;
double avgDeviation = _state.SumDeviation / currentCount;
double bandWidth = _multiplier * avgDeviation;
Last = new TValue(lastItem.Time, middle);
Upper = new TValue(lastItem.Time, middle + bandWidth);
Lower = new TValue(lastItem.Time, middle - bandWidth);
}
_pState = _state;
}
/// <summary>
/// Resets the indicator state.
/// </summary>
public void Reset()
{
_sourceBuffer.Clear();
_deviationBuffer.Clear();
_state = default;
_pState = default;
Last = default;
Upper = default;
Lower = default;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Static Batch Methods
/////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Output buffers for batch Aberr calculation.
/// </summary>
#pragma warning disable MA0077 // ref struct cannot implement interfaces
[StructLayout(LayoutKind.Auto)]
#pragma warning disable S1104 // Fields should not have public accessibility
public readonly ref struct BatchOutputs
{
/// <summary>Output middle band (SMA of source)</summary>
public readonly Span<double> Middle;
/// <summary>Output upper band</summary>
public readonly Span<double> Upper;
/// <summary>Output lower band</summary>
public readonly Span<double> Lower;
#pragma warning restore S1104
/// <summary>
/// Creates a new BatchOutputs instance.
/// </summary>
public BatchOutputs(Span<double> middle, Span<double> upper, Span<double> lower)
{
Middle = middle;
Upper = upper;
Lower = lower;
}
public bool Equals(BatchOutputs other) =>
Unsafe.AreSame(ref MemoryMarshal.GetReference(Middle), ref MemoryMarshal.GetReference(other.Middle)) &&
Middle.Length == other.Middle.Length &&
Unsafe.AreSame(ref MemoryMarshal.GetReference(Upper), ref MemoryMarshal.GetReference(other.Upper)) &&
Upper.Length == other.Upper.Length &&
Unsafe.AreSame(ref MemoryMarshal.GetReference(Lower), ref MemoryMarshal.GetReference(other.Lower)) &&
Lower.Length == other.Lower.Length;
public override bool Equals(object? obj) => false;
public override int GetHashCode() => HashCode.Combine(Middle.Length, Upper.Length, Lower.Length);
public static bool operator ==(BatchOutputs left, BatchOutputs right) => left.Equals(right);
public static bool operator !=(BatchOutputs left, BatchOutputs right) => !left.Equals(right);
}
#pragma warning restore MA0077
/// <summary>
/// Resync interval for batch path only (streaming uses Kahan compensation).
/// </summary>
private const int ResyncInterval = 1000;
/// <summary>
/// Internal state for scalar calculation.
/// </summary>
#pragma warning disable MA0077 // ref struct cannot implement interfaces
[StructLayout(LayoutKind.Auto)]
[SuppressMessage("NDepend", "ND1903:StructuresShouldBeImmutable", Justification = "Mutable calculation state accumulator by design")]
private ref struct ScalarState
{
internal double SumSource;
internal double SumDeviation;
internal double LastValidValue;
internal int BufferIndex;
internal int TickCount;
public bool Equals(ScalarState other) =>
SumSource.Equals(other.SumSource) && SumDeviation.Equals(other.SumDeviation) &&
LastValidValue.Equals(other.LastValidValue) && BufferIndex == other.BufferIndex &&
TickCount == other.TickCount;
public override bool Equals(object? obj) => false;
public override int GetHashCode() => HashCode.Combine(SumSource, SumDeviation, LastValidValue, BufferIndex, TickCount);
public static bool operator ==(ScalarState left, ScalarState right) => left.Equals(right);
public static bool operator !=(ScalarState left, ScalarState right) => !left.Equals(right);
}
#pragma warning restore MA0077
/// <summary>
/// Working buffers for batch calculation.
/// </summary>
#pragma warning disable MA0077 // ref struct cannot implement interfaces
[StructLayout(LayoutKind.Auto)]
private readonly ref struct WorkBuffers(Span<double> source, Span<double> deviation)
{
internal readonly Span<double> Source = source;
internal readonly Span<double> Deviation = deviation;
public bool Equals(WorkBuffers other) =>
Unsafe.AreSame(ref MemoryMarshal.GetReference(Source), ref MemoryMarshal.GetReference(other.Source)) &&
Source.Length == other.Source.Length &&
Unsafe.AreSame(ref MemoryMarshal.GetReference(Deviation), ref MemoryMarshal.GetReference(other.Deviation)) &&
Deviation.Length == other.Deviation.Length;
public override bool Equals(object? obj) => false;
public override int GetHashCode() => HashCode.Combine(Source.Length, Deviation.Length);
public static bool operator ==(WorkBuffers left, WorkBuffers right) => left.Equals(right);
public static bool operator !=(WorkBuffers left, WorkBuffers right) => !left.Equals(right);
}
#pragma warning restore MA0077
/// <summary>
/// Calculates Aberr for the entire TSeries using a new instance.
/// </summary>
public static (TSeries Middle, TSeries Upper, TSeries Lower) Batch(TSeries source, int period, double multiplier = 2.0)
{
var aberr = new Aberr(period, multiplier);
return aberr.Update(source);
}
/// <summary>
/// Calculates Aberr in-place using spans for maximum performance.
/// Zero-allocation method.
/// </summary>
/// <param name="source">Source price values</param>
/// <param name="outputs">Output buffers for middle, upper, and lower bands</param>
/// <param name="period">Lookback period</param>
/// <param name="multiplier">Band width multiplier</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Batch(
ReadOnlySpan<double> source,
BatchOutputs outputs,
int period,
double multiplier = 2.0)
{
Batch(source, outputs.Middle, outputs.Upper, outputs.Lower, period, multiplier);
}
/// <summary>
/// Calculates Aberr in-place using spans for maximum performance.
/// Zero-allocation method.
/// </summary>
/// <param name="source">Source price values</param>
/// <param name="middle">Output middle band (SMA of source)</param>
/// <param name="upper">Output upper band</param>
/// <param name="lower">Output lower band</param>
/// <param name="period">Lookback period</param>
/// <param name="multiplier">Band width multiplier</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Batch(
ReadOnlySpan<double> source,
Span<double> middle,
Span<double> upper,
Span<double> lower,
int period,
double multiplier = 2.0)
{
int len = source.Length;
if (middle.Length < len || upper.Length < len || lower.Length < len)
{
throw new ArgumentException("Output buffers must be at least as long as input", nameof(middle));
}
if (period <= 0)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
}
if (multiplier <= 0)
{
throw new ArgumentOutOfRangeException(nameof(multiplier), "Multiplier must be greater than 0");
}
if (len == 0)
{
return;
}
// Scalar implementation with NaN handling
var outputs = new BatchOutputs(middle, upper, lower);
CalculateScalarCore(source, outputs, period, multiplier);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CalculateScalarCore(
ReadOnlySpan<double> source,
scoped BatchOutputs outputs,
int period,
double multiplier)
{
int len = source.Length;
// Always use ArrayPool to avoid span scope safety issues with stackalloc + ref structs
double[] rentedSource = ArrayPool<double>.Shared.Rent(period);
double[] rentedDeviation = ArrayPool<double>.Shared.Rent(period);
try
{
var buffers = new WorkBuffers(
rentedSource.AsSpan(0, period),
rentedDeviation.AsSpan(0, period));
var state = new ScalarState
{
LastValidValue = double.NaN,
};
SeedFirstValidValue(source, ref state);
int warmupEnd = Math.Min(period, len);
ProcessWarmupPhase(source, outputs, warmupEnd, multiplier, ref buffers, ref state);
ProcessMainLoop(source, outputs, warmupEnd, period, multiplier, ref buffers, ref state);
}
finally
{
ArrayPool<double>.Shared.Return(rentedSource);
ArrayPool<double>.Shared.Return(rentedDeviation);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SeedFirstValidValue(ReadOnlySpan<double> source, ref ScalarState state)
{
int len = source.Length;
for (int k = 0; k < len; k++)
{
if (double.IsFinite(source[k]))
{
state.LastValidValue = source[k];
break;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double GetValidValue(ReadOnlySpan<double> source, int i, ref ScalarState state)
{
double v = source[i];
if (double.IsFinite(v))
{
state.LastValidValue = v;
return v;
}
return state.LastValidValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WriteBandOutputs(scoped BatchOutputs outputs, int i, double middle, double avgDeviation, double multiplier)
{
double bandWidth = multiplier * avgDeviation;
outputs.Middle[i] = middle;
outputs.Upper[i] = middle + bandWidth;
outputs.Lower[i] = middle - bandWidth;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ProcessWarmupPhase(
ReadOnlySpan<double> source,
scoped BatchOutputs outputs,
int warmupEnd,
double multiplier,
ref WorkBuffers buffers,
ref ScalarState state)
{
for (int i = 0; i < warmupEnd; i++)
{
double v = GetValidValue(source, i, ref state);
// Compute SMA including the new value to get same-bar deviation (matches Pine)
int newCount = i + 1;
double newSum = state.SumSource + v;
double sma = newSum / newCount;
double deviation = Math.Abs(v - sma);
state.SumSource = newSum;
state.SumDeviation += deviation;
buffers.Source[i] = v;
buffers.Deviation[i] = deviation;
double middle = state.SumSource / newCount;
double avgDeviation = state.SumDeviation / newCount;
WriteBandOutputs(outputs, i, middle, avgDeviation, multiplier);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ProcessMainLoop(
ReadOnlySpan<double> source,
scoped BatchOutputs outputs,
int startIndex,
int period,
double multiplier,
ref WorkBuffers buffers,
ref ScalarState state)
{
int len = source.Length;
for (int i = startIndex; i < len; i++)
{
double v = GetValidValue(source, i, ref state);
// Compute SMA including the new value to get same-bar deviation (matches Pine)
double newSumSource = state.SumSource - buffers.Source[state.BufferIndex] + v;
double sma = newSumSource / period;
double deviation = Math.Abs(v - sma);
// Update running sums using single buffer index
state.SumSource = newSumSource;
buffers.Source[state.BufferIndex] = v;
state.SumDeviation = state.SumDeviation - buffers.Deviation[state.BufferIndex] + deviation;
buffers.Deviation[state.BufferIndex] = deviation;
state.BufferIndex++;
if (state.BufferIndex >= period)
{
state.BufferIndex = 0;
}
double middle = state.SumSource / period;
double avgDeviation = state.SumDeviation / period;
WriteBandOutputs(outputs, i, middle, avgDeviation, multiplier);
state.TickCount++;
if (state.TickCount >= ResyncInterval)
{
ResyncSums(period, ref buffers, ref state);
}
}
}
/// <summary>
/// Runs a high-performance batch calculation and returns a "Hot" Aberr instance.
/// </summary>
public static ((TSeries Middle, TSeries Upper, TSeries Lower) Results, Aberr Indicator) Calculate(TSeries source, int period, double multiplier = 2.0)
{
var aberr = new Aberr(period, multiplier);
var results = aberr.Update(source);
return (results, aberr);
}
/// <summary>
/// Disposes the Aberr instance, unsubscribing from the source publisher.
/// This method is idempotent.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
if (_source != null)
{
_source.Pub -= _handler;
_source = null;
}
_disposed = true;
}
GC.SuppressFinalize(this);
}
}