@synnaxlabs/x 0.44.2 → 0.44.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +15 -15
- package/dist/bounds-8OC_obRs.js +186 -0
- package/dist/bounds-CRK04jp7.cjs +1 -0
- package/dist/bounds.cjs +1 -1
- package/dist/bounds.js +1 -1
- package/dist/deep.cjs +1 -1
- package/dist/deep.js +1 -1
- package/dist/{external-Birv9jaY.js → external-BM_NS5yM.js} +6 -6
- package/dist/external-E3ErJeeM.cjs +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.js +235 -229
- package/dist/{path-DVFrKaNI.js → path-Blh4wJuA.js} +24 -21
- package/dist/path-CPSfCjde.cjs +1 -0
- package/dist/{scale-C6qKDbRb.cjs → scale-C3fEtXxW.cjs} +1 -1
- package/dist/{scale-EWNUk-bn.js → scale-Db1Gunj0.js} +1 -1
- package/dist/scale.cjs +1 -1
- package/dist/scale.js +1 -1
- package/dist/series-BcF7A8Je.cjs +6 -0
- package/dist/{series-EA1uaEDj.js → series-Cl3Vh_u-.js} +588 -471
- package/dist/spatial.cjs +1 -1
- package/dist/spatial.js +2 -2
- package/dist/src/breaker/breaker.d.ts +2 -1
- package/dist/src/breaker/breaker.d.ts.map +1 -1
- package/dist/src/deep/path.d.ts.map +1 -1
- package/dist/src/id/id.d.ts +3 -1
- package/dist/src/id/id.d.ts.map +1 -1
- package/dist/src/math/math.d.ts +2 -1
- package/dist/src/math/math.d.ts.map +1 -1
- package/dist/src/status/status.d.ts +1 -0
- package/dist/src/status/status.d.ts.map +1 -1
- package/dist/src/strings/strings.d.ts +1 -1
- package/dist/src/strings/strings.d.ts.map +1 -1
- package/dist/src/telem/series.d.ts +7 -0
- package/dist/src/telem/series.d.ts.map +1 -1
- package/dist/src/telem/telem.d.ts +78 -1
- package/dist/src/telem/telem.d.ts.map +1 -1
- package/dist/src/zod/util.d.ts.map +1 -1
- package/dist/telem.cjs +1 -1
- package/dist/telem.js +1 -1
- package/dist/zod.cjs +1 -1
- package/dist/zod.js +1 -1
- package/package.json +3 -3
- package/src/breaker/breaker.ts +4 -0
- package/src/deep/path.spec.ts +14 -0
- package/src/deep/path.ts +9 -2
- package/src/id/id.ts +8 -4
- package/src/math/math.spec.ts +20 -0
- package/src/math/math.ts +32 -29
- package/src/spatial/bounds/bounds.ts +1 -1
- package/src/status/status.ts +11 -0
- package/src/strings/strings.spec.ts +3 -0
- package/src/strings/strings.ts +2 -1
- package/src/telem/series.spec.ts +543 -2
- package/src/telem/series.ts +28 -9
- package/src/telem/telem.spec.ts +556 -0
- package/src/telem/telem.ts +118 -5
- package/src/zod/util.ts +5 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/bounds-D6e9xoHt.cjs +0 -1
- package/dist/bounds-Dj9nG39I.js +0 -174
- package/dist/external-DsmsSN1Y.cjs +0 -1
- package/dist/path-BeMr8xWN.cjs +0 -1
- package/dist/series-CcA_WjbJ.cjs +0 -6
package/src/telem/series.spec.ts
CHANGED
|
@@ -778,6 +778,154 @@ describe("Series", () => {
|
|
|
778
778
|
}).toThrow();
|
|
779
779
|
});
|
|
780
780
|
});
|
|
781
|
+
|
|
782
|
+
describe("property preservation during conversion", () => {
|
|
783
|
+
it("should preserve all properties when converting a series with .as()", () => {
|
|
784
|
+
// Create a series with all possible properties set
|
|
785
|
+
const timeRange = new TimeRange(TimeStamp.seconds(100), TimeStamp.seconds(200));
|
|
786
|
+
const original = new Series({
|
|
787
|
+
data: new Float32Array([1, 2, 3, 4, 5]),
|
|
788
|
+
dataType: DataType.FLOAT32,
|
|
789
|
+
timeRange,
|
|
790
|
+
sampleOffset: 10,
|
|
791
|
+
alignment: 100n,
|
|
792
|
+
alignmentMultiple: 5n,
|
|
793
|
+
key: "test-series-key",
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// Convert to number type using .as()
|
|
797
|
+
const converted = original.as("number");
|
|
798
|
+
|
|
799
|
+
// Verify all properties are preserved
|
|
800
|
+
expect(converted.dataType).toEqual(original.dataType);
|
|
801
|
+
expect(converted.timeRange).toBe(original.timeRange);
|
|
802
|
+
expect(converted.sampleOffset).toBe(10);
|
|
803
|
+
expect(converted.alignment).toBe(100n);
|
|
804
|
+
expect(converted.alignmentMultiple).toBe(5n);
|
|
805
|
+
expect(converted.key).toBe("test-series-key");
|
|
806
|
+
expect(converted.length).toBe(5);
|
|
807
|
+
expect(converted.alignmentBounds).toEqual({ lower: 100n, upper: 125n });
|
|
808
|
+
|
|
809
|
+
// Verify data is still accessible and correct with offset applied
|
|
810
|
+
expect(converted.at(0)).toBe(11); // 1 + sampleOffset(10)
|
|
811
|
+
expect(converted.at(1)).toBe(12); // 2 + sampleOffset(10)
|
|
812
|
+
expect(converted.at(4)).toBe(15); // 5 + sampleOffset(10)
|
|
813
|
+
|
|
814
|
+
// Verify alignment access still works
|
|
815
|
+
expect(converted.atAlignment(100n)).toBe(11);
|
|
816
|
+
expect(converted.atAlignment(105n)).toBe(12);
|
|
817
|
+
expect(converted.atAlignment(120n)).toBe(15);
|
|
818
|
+
|
|
819
|
+
// Verify buffer is the same (no copy)
|
|
820
|
+
expect(converted.buffer).toBe(original.buffer);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
it("should preserve properties when converting between different JS types", () => {
|
|
824
|
+
const timeRange = new TimeRange(TimeStamp.seconds(50), TimeStamp.seconds(150));
|
|
825
|
+
|
|
826
|
+
// Test with bigint series
|
|
827
|
+
const bigintSeries = new Series({
|
|
828
|
+
data: [100n, 200n, 300n],
|
|
829
|
+
dataType: DataType.INT64,
|
|
830
|
+
timeRange,
|
|
831
|
+
sampleOffset: 1000n,
|
|
832
|
+
alignment: 50n,
|
|
833
|
+
alignmentMultiple: 10n,
|
|
834
|
+
key: "bigint-series",
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
const bigintConverted = bigintSeries.as("bigint");
|
|
838
|
+
expect(bigintConverted.timeRange).toBe(timeRange);
|
|
839
|
+
expect(bigintConverted.sampleOffset).toBe(1000n);
|
|
840
|
+
expect(bigintConverted.alignment).toBe(50n);
|
|
841
|
+
expect(bigintConverted.alignmentMultiple).toBe(10n);
|
|
842
|
+
expect(bigintConverted.key).toBe("bigint-series");
|
|
843
|
+
expect(bigintConverted.at(0)).toBe(1100n); // 100n + 1000n
|
|
844
|
+
expect(bigintConverted.alignmentBounds).toEqual({ lower: 50n, upper: 80n });
|
|
845
|
+
|
|
846
|
+
// Test with string series
|
|
847
|
+
const stringSeries = new Series({
|
|
848
|
+
data: ["apple", "banana", "cherry"],
|
|
849
|
+
dataType: DataType.STRING,
|
|
850
|
+
timeRange,
|
|
851
|
+
alignment: 200n,
|
|
852
|
+
alignmentMultiple: 3n,
|
|
853
|
+
key: "string-series",
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
const stringConverted = stringSeries.as("string");
|
|
857
|
+
expect(stringConverted.timeRange).toBe(timeRange);
|
|
858
|
+
expect(stringConverted.alignment).toBe(200n);
|
|
859
|
+
expect(stringConverted.alignmentMultiple).toBe(3n);
|
|
860
|
+
expect(stringConverted.key).toBe("string-series");
|
|
861
|
+
expect(stringConverted.at(0)).toBe("apple");
|
|
862
|
+
expect(stringConverted.alignmentBounds).toEqual({ lower: 200n, upper: 209n });
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
it("should preserve properties when converting UUID series to string", () => {
|
|
866
|
+
const timeRange = new TimeRange(TimeStamp.seconds(10), TimeStamp.seconds(20));
|
|
867
|
+
const uuidSeries = new Series({
|
|
868
|
+
data: SAMPLE_UUID_BYTES,
|
|
869
|
+
dataType: DataType.UUID,
|
|
870
|
+
timeRange,
|
|
871
|
+
alignment: 1000n,
|
|
872
|
+
alignmentMultiple: 100n,
|
|
873
|
+
key: "uuid-series-key",
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
const stringConverted = uuidSeries.as("string");
|
|
877
|
+
|
|
878
|
+
// All non-data properties should be preserved
|
|
879
|
+
expect(stringConverted.dataType).toEqual(DataType.UUID);
|
|
880
|
+
expect(stringConverted.timeRange).toBe(timeRange);
|
|
881
|
+
expect(stringConverted.alignment).toBe(1000n);
|
|
882
|
+
expect(stringConverted.alignmentMultiple).toBe(100n);
|
|
883
|
+
expect(stringConverted.key).toBe("uuid-series-key");
|
|
884
|
+
expect(stringConverted.length).toBe(2);
|
|
885
|
+
expect(stringConverted.alignmentBounds).toEqual({ lower: 1000n, upper: 1200n });
|
|
886
|
+
|
|
887
|
+
// Verify data access still works
|
|
888
|
+
expect(stringConverted.at(0)).toBe("123e4567-e89b-40d3-8056-426614174000");
|
|
889
|
+
expect(stringConverted.atAlignment(1000n)).toBe(
|
|
890
|
+
"123e4567-e89b-40d3-8056-426614174000",
|
|
891
|
+
);
|
|
892
|
+
expect(stringConverted.atAlignment(1100n)).toBe(
|
|
893
|
+
"7f3e4567-e89b-40d3-8056-426614174000",
|
|
894
|
+
);
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
it("should preserve properties when creating a new series from an existing one", () => {
|
|
898
|
+
const timeRange = new TimeRange(TimeStamp.seconds(1), TimeStamp.seconds(10));
|
|
899
|
+
const original = new Series({
|
|
900
|
+
data: [1.5, 2.5, 3.5],
|
|
901
|
+
dataType: DataType.FLOAT64,
|
|
902
|
+
timeRange,
|
|
903
|
+
sampleOffset: 0.5,
|
|
904
|
+
alignment: 15n,
|
|
905
|
+
alignmentMultiple: 2n,
|
|
906
|
+
key: "original-key",
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// Create new series from existing one
|
|
910
|
+
const copy = new Series(original);
|
|
911
|
+
|
|
912
|
+
// All properties should be preserved
|
|
913
|
+
expect(copy.dataType).toEqual(original.dataType);
|
|
914
|
+
expect(copy.timeRange).toBe(original.timeRange);
|
|
915
|
+
expect(copy.sampleOffset).toBe(0.5);
|
|
916
|
+
expect(copy.alignment).toBe(15n);
|
|
917
|
+
expect(copy.alignmentMultiple).toBe(2n);
|
|
918
|
+
expect(copy.key).toBe("original-key"); // Key is preserved when creating from existing series
|
|
919
|
+
expect(copy.length).toBe(3);
|
|
920
|
+
expect(copy.alignmentBounds).toEqual({ lower: 15n, upper: 21n });
|
|
921
|
+
expect(copy.buffer).toBe(original.buffer); // Should share buffer
|
|
922
|
+
|
|
923
|
+
// Data access with offset
|
|
924
|
+
expect(copy.at(0)).toBe(2); // 1.5 + 0.5
|
|
925
|
+
expect(copy.at(1)).toBe(3); // 2.5 + 0.5
|
|
926
|
+
expect(copy.at(2)).toBe(4); // 3.5 + 0.5
|
|
927
|
+
});
|
|
928
|
+
});
|
|
781
929
|
});
|
|
782
930
|
|
|
783
931
|
describe("alignmentBounds", () => {
|
|
@@ -791,6 +939,204 @@ describe("Series", () => {
|
|
|
791
939
|
});
|
|
792
940
|
});
|
|
793
941
|
|
|
942
|
+
describe("alignmentMultiple", () => {
|
|
943
|
+
it("should default to 1n when not specified", () => {
|
|
944
|
+
const series = new Series({
|
|
945
|
+
data: new Float32Array([1, 2, 3]),
|
|
946
|
+
alignment: 10n,
|
|
947
|
+
});
|
|
948
|
+
expect(series.alignmentMultiple).toBe(1n);
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
it("should be set correctly when specified in constructor", () => {
|
|
952
|
+
const series = new Series({
|
|
953
|
+
data: new Float32Array([1, 2, 3]),
|
|
954
|
+
alignment: 10n,
|
|
955
|
+
alignmentMultiple: 5n,
|
|
956
|
+
});
|
|
957
|
+
expect(series.alignmentMultiple).toBe(5n);
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
it("should correctly calculate alignment bounds with alignmentMultiple", () => {
|
|
961
|
+
const series = new Series({
|
|
962
|
+
data: new Float32Array([1, 2, 3]),
|
|
963
|
+
alignment: 10n,
|
|
964
|
+
alignmentMultiple: 5n,
|
|
965
|
+
});
|
|
966
|
+
// lower: 10n (alignment)
|
|
967
|
+
// upper: 10n + 3n * 5n = 25n
|
|
968
|
+
expect(series.alignmentBounds).toEqual({ lower: 10n, upper: 25n });
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it("should correctly calculate alignment bounds with alignmentMultiple = 1", () => {
|
|
972
|
+
const series = new Series({
|
|
973
|
+
data: new Float32Array([1, 2, 3]),
|
|
974
|
+
alignment: 10n,
|
|
975
|
+
alignmentMultiple: 1n,
|
|
976
|
+
});
|
|
977
|
+
// lower: 10n (alignment)
|
|
978
|
+
// upper: 10n + 3n * 1n = 13n
|
|
979
|
+
expect(series.alignmentBounds).toEqual({ lower: 10n, upper: 13n });
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
it("should work with atAlignment when alignmentMultiple > 1", () => {
|
|
983
|
+
const series = new Series({
|
|
984
|
+
data: new Float32Array([10, 20, 30, 40, 50]),
|
|
985
|
+
alignment: 100n,
|
|
986
|
+
alignmentMultiple: 10n,
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
expect(series.atAlignment(100n)).toBe(10);
|
|
990
|
+
expect(series.atAlignment(110n)).toBe(20);
|
|
991
|
+
expect(series.atAlignment(120n)).toBe(30);
|
|
992
|
+
expect(series.atAlignment(130n)).toBe(40);
|
|
993
|
+
expect(series.atAlignment(140n)).toBe(50);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
it("should return undefined for alignments not on the multiple grid", () => {
|
|
997
|
+
const series = new Series({
|
|
998
|
+
data: new Float32Array([10, 20, 30]),
|
|
999
|
+
alignment: 100n,
|
|
1000
|
+
alignmentMultiple: 10n,
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
// These alignments are not on the 10n grid starting from 100n
|
|
1004
|
+
expect(series.atAlignment(105n)).toBe(10);
|
|
1005
|
+
expect(series.atAlignment(115n)).toBe(20);
|
|
1006
|
+
expect(series.atAlignment(125n)).toBe(30);
|
|
1007
|
+
expect(series.atAlignment(135n)).toBeUndefined();
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it("should handle alignmentMultiple with decimation scenario", () => {
|
|
1011
|
+
const series = new Series({
|
|
1012
|
+
data: new Float32Array([1, 5, 9, 13, 17]),
|
|
1013
|
+
alignment: 0n,
|
|
1014
|
+
alignmentMultiple: 4n,
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
expect(series.alignmentBounds).toEqual({ lower: 0n, upper: 20n });
|
|
1018
|
+
expect(series.atAlignment(0n)).toBe(1);
|
|
1019
|
+
expect(series.atAlignment(4n)).toBe(5);
|
|
1020
|
+
expect(series.atAlignment(8n)).toBe(9);
|
|
1021
|
+
expect(series.atAlignment(12n)).toBe(13);
|
|
1022
|
+
expect(series.atAlignment(16n)).toBe(17);
|
|
1023
|
+
|
|
1024
|
+
expect(series.atAlignment(1n)).toBe(1);
|
|
1025
|
+
expect(series.atAlignment(2n)).toBe(1);
|
|
1026
|
+
expect(series.atAlignment(3n)).toBe(1);
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
it("should handle alignmentMultiple with averaging scenario", () => {
|
|
1030
|
+
const series = new Series({
|
|
1031
|
+
data: new Float32Array([10, 20, 30]),
|
|
1032
|
+
alignment: 1000n,
|
|
1033
|
+
alignmentMultiple: 10n,
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
expect(series.alignmentBounds).toEqual({ lower: 1000n, upper: 1030n });
|
|
1037
|
+
|
|
1038
|
+
expect(series.atAlignment(1000n)).toBe(10);
|
|
1039
|
+
expect(series.atAlignment(1010n)).toBe(20);
|
|
1040
|
+
expect(series.atAlignment(1020n)).toBe(30);
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
it("should preserve alignmentMultiple when creating from another series", () => {
|
|
1044
|
+
const original = new Series({
|
|
1045
|
+
data: new Float32Array([1, 2, 3]),
|
|
1046
|
+
alignment: 100n,
|
|
1047
|
+
alignmentMultiple: 5n,
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
const copy = new Series(original);
|
|
1051
|
+
expect(copy.alignmentMultiple).toBe(5n);
|
|
1052
|
+
expect(copy.alignment).toBe(100n);
|
|
1053
|
+
expect(copy.alignmentBounds).toEqual(original.alignmentBounds);
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
it("should handle negative alignment indices correctly with alignmentMultiple", () => {
|
|
1057
|
+
const series = new Series({
|
|
1058
|
+
data: new Float32Array([10, 20, 30]),
|
|
1059
|
+
alignment: 50n,
|
|
1060
|
+
alignmentMultiple: 3n,
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
expect(series.atAlignment(45n)).toBeUndefined();
|
|
1064
|
+
expect(series.atAlignment(47n)).toBeUndefined();
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
it("should handle alignment beyond series end with alignmentMultiple", () => {
|
|
1068
|
+
const series = new Series({
|
|
1069
|
+
data: new Float32Array([10, 20]),
|
|
1070
|
+
alignment: 0n,
|
|
1071
|
+
alignmentMultiple: 5n,
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
expect(series.atAlignment(0n)).toBe(10);
|
|
1075
|
+
expect(series.atAlignment(5n)).toBe(20);
|
|
1076
|
+
expect(series.atAlignment(10n)).toBeUndefined();
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
it("should throw error when required=true and alignment not found with alignmentMultiple", () => {
|
|
1080
|
+
const series = new Series({
|
|
1081
|
+
data: new Float32Array([10, 20]),
|
|
1082
|
+
alignment: 100n,
|
|
1083
|
+
alignmentMultiple: 10n,
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
expect(() => series.atAlignment(150n, true)).toThrow();
|
|
1087
|
+
expect(() => series.atAlignment(90n, true)).toThrow();
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
it("should work with very large alignmentMultiple values", () => {
|
|
1091
|
+
const series = new Series({
|
|
1092
|
+
data: new Float32Array([1, 2]),
|
|
1093
|
+
alignment: 1000000000n,
|
|
1094
|
+
alignmentMultiple: 1000000n,
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
expect(series.atAlignment(1000000000n)).toBe(1);
|
|
1098
|
+
expect(series.atAlignment(1001000000n)).toBe(2);
|
|
1099
|
+
expect(series.alignmentBounds).toEqual({
|
|
1100
|
+
lower: 1000000000n,
|
|
1101
|
+
upper: 1002000000n,
|
|
1102
|
+
});
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it("should handle alignmentMultiple with different data types", () => {
|
|
1106
|
+
const stringSeries = new Series({
|
|
1107
|
+
data: ["a", "b", "c"],
|
|
1108
|
+
alignment: 10n,
|
|
1109
|
+
alignmentMultiple: 2n,
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
expect(stringSeries.atAlignment(10n)).toBe("a");
|
|
1113
|
+
expect(stringSeries.atAlignment(12n)).toBe("b");
|
|
1114
|
+
expect(stringSeries.atAlignment(14n)).toBe("c");
|
|
1115
|
+
|
|
1116
|
+
const jsonSeries = new Series({
|
|
1117
|
+
data: [{ x: 1 }, { x: 2 }],
|
|
1118
|
+
alignment: 5n,
|
|
1119
|
+
alignmentMultiple: 3n,
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
expect(jsonSeries.atAlignment(5n)).toEqual({ x: 1 });
|
|
1123
|
+
expect(jsonSeries.atAlignment(8n)).toEqual({ x: 2 });
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it("should correctly calculate index from alignment with alignmentMultiple", () => {
|
|
1127
|
+
const series = new Series({
|
|
1128
|
+
data: new Float32Array([100, 200, 300, 400]),
|
|
1129
|
+
alignment: 50n,
|
|
1130
|
+
alignmentMultiple: 25n,
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
expect(series.atAlignment(50n)).toBe(100);
|
|
1134
|
+
expect(series.atAlignment(75n)).toBe(200);
|
|
1135
|
+
expect(series.atAlignment(100n)).toBe(300);
|
|
1136
|
+
expect(series.atAlignment(125n)).toBe(400);
|
|
1137
|
+
});
|
|
1138
|
+
});
|
|
1139
|
+
|
|
794
1140
|
describe("toStrings", () => {
|
|
795
1141
|
interface Spec {
|
|
796
1142
|
name: string;
|
|
@@ -975,9 +1321,20 @@ describe("Series", () => {
|
|
|
975
1321
|
expect(iter.next().value).toEqual(3);
|
|
976
1322
|
expect(iter.next().value).toEqual(4);
|
|
977
1323
|
});
|
|
1324
|
+
|
|
1325
|
+
it("should iterate over the whole series", () => {
|
|
1326
|
+
const s = new Series(new Float32Array([1, 2, 3, 4, 5]));
|
|
1327
|
+
const iter = s.subIterator(0, 6);
|
|
1328
|
+
expect(iter.next().value).toEqual(1);
|
|
1329
|
+
expect(iter.next().value).toEqual(2);
|
|
1330
|
+
expect(iter.next().value).toEqual(3);
|
|
1331
|
+
expect(iter.next().value).toEqual(4);
|
|
1332
|
+
expect(iter.next().value).toEqual(5);
|
|
1333
|
+
expect(iter.next().done).toBe(true);
|
|
1334
|
+
});
|
|
978
1335
|
});
|
|
979
1336
|
|
|
980
|
-
describe("
|
|
1337
|
+
describe("subAlignmentIterator", () => {
|
|
981
1338
|
it("should return an iterator over a sub-series", () => {
|
|
982
1339
|
const s = new Series({
|
|
983
1340
|
data: new Float32Array([1, 2, 3, 4, 5]),
|
|
@@ -988,9 +1345,10 @@ describe("Series", () => {
|
|
|
988
1345
|
expect(iter.next().value).toEqual(3);
|
|
989
1346
|
expect(iter.next().done).toBe(true);
|
|
990
1347
|
});
|
|
1348
|
+
|
|
991
1349
|
it("should clamp the bounds to the alignment", () => {
|
|
992
1350
|
const s = new Series({
|
|
993
|
-
data: new Float32Array([1, 2, 3, 4, 5]),
|
|
1351
|
+
data: new Float32Array([1, 2, 3, 4, 5]), // 2n, 3n, 4n, 5n,
|
|
994
1352
|
alignment: 2n,
|
|
995
1353
|
});
|
|
996
1354
|
const iter = s.subAlignmentIterator(1n, 5n);
|
|
@@ -999,6 +1357,102 @@ describe("Series", () => {
|
|
|
999
1357
|
expect(iter.next().value).toEqual(3);
|
|
1000
1358
|
expect(iter.next().done).toBe(true);
|
|
1001
1359
|
});
|
|
1360
|
+
|
|
1361
|
+
it("should handle series with alignment multiple", () => {
|
|
1362
|
+
const s = new Series({
|
|
1363
|
+
// 2n, 4n, 6n, 8n, 10n,
|
|
1364
|
+
data: new Float32Array([1, 2, 3, 4, 5]),
|
|
1365
|
+
alignment: 2n,
|
|
1366
|
+
alignmentMultiple: 2n,
|
|
1367
|
+
});
|
|
1368
|
+
const iter = s.subAlignmentIterator(1n, 5n);
|
|
1369
|
+
// (1n - 2n) / 2n = 0
|
|
1370
|
+
// (5n - 2n) / 2n = 1
|
|
1371
|
+
expect(iter.next().value).toEqual(1);
|
|
1372
|
+
expect(iter.next().value).toEqual(2);
|
|
1373
|
+
expect(iter.next().value).toBeUndefined();
|
|
1374
|
+
expect(iter.next().done).toBe(true);
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
it("should correctly iterate over a series with alignmentMultiple > 1", () => {
|
|
1378
|
+
const series = new Series({
|
|
1379
|
+
data: new Float32Array([10, 20, 30, 40, 50]),
|
|
1380
|
+
alignment: 100n,
|
|
1381
|
+
alignmentMultiple: 10n,
|
|
1382
|
+
});
|
|
1383
|
+
const iter = series.subAlignmentIterator(110n, 140n);
|
|
1384
|
+
expect(iter.next().value).toBe(20);
|
|
1385
|
+
expect(iter.next().value).toBe(30);
|
|
1386
|
+
expect(iter.next().value).toBe(40);
|
|
1387
|
+
expect(iter.next().done).toBe(true);
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
it("should handle partial ranges with alignmentMultiple", () => {
|
|
1391
|
+
const series = new Series({
|
|
1392
|
+
data: new Float32Array([1, 2, 3, 4]),
|
|
1393
|
+
alignment: 0n,
|
|
1394
|
+
alignmentMultiple: 5n,
|
|
1395
|
+
});
|
|
1396
|
+
// Series covers alignments 0, 5, 10, 15
|
|
1397
|
+
const iter = series.subAlignmentIterator(3n, 12n);
|
|
1398
|
+
expect(iter.next().value).toBe(2); // alignment 5
|
|
1399
|
+
expect(iter.next().value).toBe(3); // alignment 10
|
|
1400
|
+
expect(iter.next().done).toBe(true);
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
it("should handle when start alignment doesn't align with the multiple grid", () => {
|
|
1404
|
+
const series = new Series({
|
|
1405
|
+
data: new Float32Array([100, 200, 300]),
|
|
1406
|
+
alignment: 50n,
|
|
1407
|
+
alignmentMultiple: 25n,
|
|
1408
|
+
});
|
|
1409
|
+
// Series covers alignments 50, 75, 100
|
|
1410
|
+
const iter = series.subAlignmentIterator(60n, 95n);
|
|
1411
|
+
expect(iter.next().value).toBe(200); // alignment 75
|
|
1412
|
+
expect(iter.next().done).toBe(true);
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
it("should return empty iterator when range is outside series bounds", () => {
|
|
1416
|
+
const series = new Series({
|
|
1417
|
+
data: new Float32Array([1, 2, 3]),
|
|
1418
|
+
alignment: 100n,
|
|
1419
|
+
alignmentMultiple: 10n,
|
|
1420
|
+
});
|
|
1421
|
+
// Series covers alignments 100, 110, 120
|
|
1422
|
+
const iter = series.subAlignmentIterator(200n, 300n);
|
|
1423
|
+
expect(iter.next().done).toBe(true);
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
it("should handle decimation scenario with alignmentMultiple", () => {
|
|
1427
|
+
// Simulating decimated data where every 4th sample is kept
|
|
1428
|
+
const series = new Series({
|
|
1429
|
+
data: new Float32Array([1, 5, 9, 13, 17]),
|
|
1430
|
+
alignment: 0n,
|
|
1431
|
+
alignmentMultiple: 4n,
|
|
1432
|
+
});
|
|
1433
|
+
// Series covers alignments 0, 4, 8, 12, 16
|
|
1434
|
+
const iter = series.subAlignmentIterator(2n, 14n);
|
|
1435
|
+
expect(iter.next().value).toBe(5); // alignment 4
|
|
1436
|
+
expect(iter.next().value).toBe(9); // alignment 8
|
|
1437
|
+
expect(iter.next().value).toBe(13); // alignment 12
|
|
1438
|
+
expect(iter.next().done).toBe(true);
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
it("should handle averaging scenario with alignmentMultiple", () => {
|
|
1442
|
+
// Simulating averaged data where each value represents 10 samples
|
|
1443
|
+
const series = new Series({
|
|
1444
|
+
data: new Float32Array([10, 20, 30]),
|
|
1445
|
+
alignment: 1000n,
|
|
1446
|
+
alignmentMultiple: 10n,
|
|
1447
|
+
});
|
|
1448
|
+
// Series covers alignments 1000-1009, 1010-1019, 1020-1029
|
|
1449
|
+
const iter = series.subAlignmentIterator(1005n, 1025n);
|
|
1450
|
+
// Math.ceil((1005 - 1000) / 10) = 1
|
|
1451
|
+
// Math.ceil((1025 - 1000) / 10) = 3
|
|
1452
|
+
expect(iter.next().value).toBe(20); // alignment 1010
|
|
1453
|
+
expect(iter.next().value).toBe(30); // alignment 1020
|
|
1454
|
+
expect(iter.next().done).toBe(true);
|
|
1455
|
+
});
|
|
1002
1456
|
});
|
|
1003
1457
|
|
|
1004
1458
|
describe("bounds", () => {
|
|
@@ -1317,6 +1771,93 @@ describe("MultiSeries", () => {
|
|
|
1317
1771
|
expect(iter.next().value).toEqual(10);
|
|
1318
1772
|
expect(iter.next().done).toBe(true);
|
|
1319
1773
|
});
|
|
1774
|
+
|
|
1775
|
+
describe("with alignmentMultiple", () => {
|
|
1776
|
+
it("should work with MultiSeries having different alignmentMultiples", () => {
|
|
1777
|
+
const s1 = new Series({
|
|
1778
|
+
data: new Float32Array([1, 2, 3]),
|
|
1779
|
+
alignment: 0n,
|
|
1780
|
+
alignmentMultiple: 2n,
|
|
1781
|
+
});
|
|
1782
|
+
// s1 covers alignments 0, 2, 4
|
|
1783
|
+
|
|
1784
|
+
const s2 = new Series({
|
|
1785
|
+
data: new Float32Array([10, 20, 30]),
|
|
1786
|
+
alignment: 10n,
|
|
1787
|
+
alignmentMultiple: 5n,
|
|
1788
|
+
});
|
|
1789
|
+
// s2 covers alignments 10, 15, 20
|
|
1790
|
+
|
|
1791
|
+
const multi = new MultiSeries([s1, s2]);
|
|
1792
|
+
const iter = multi.subAlignmentIterator(3n, 18n);
|
|
1793
|
+
expect(iter.next().value).toBe(3); // alignment 4 from s1
|
|
1794
|
+
expect(iter.next().value).toBe(10); // alignment 10 from s2
|
|
1795
|
+
expect(iter.next().value).toBe(20); // alignment 15 from s2
|
|
1796
|
+
expect(iter.next().done).toBe(true);
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
it("should handle MultiSeries with gaps between series when using alignmentMultiple", () => {
|
|
1800
|
+
const s1 = new Series({
|
|
1801
|
+
data: new Float32Array([1, 2]),
|
|
1802
|
+
alignment: 0n,
|
|
1803
|
+
alignmentMultiple: 3n,
|
|
1804
|
+
});
|
|
1805
|
+
const s2 = new Series({
|
|
1806
|
+
data: new Float32Array([100, 200]),
|
|
1807
|
+
alignment: 100n,
|
|
1808
|
+
alignmentMultiple: 50n,
|
|
1809
|
+
});
|
|
1810
|
+
const multi = new MultiSeries([s1, s2]);
|
|
1811
|
+
const iter = multi.subAlignmentIterator(2n, 120n);
|
|
1812
|
+
expect(iter.next().value).toBe(2);
|
|
1813
|
+
expect(iter.next().value).toBe(100);
|
|
1814
|
+
expect(iter.next().done).toBe(true);
|
|
1815
|
+
});
|
|
1816
|
+
|
|
1817
|
+
it("should correctly calculate indices with very large alignmentMultiple values", () => {
|
|
1818
|
+
const series = new Series({
|
|
1819
|
+
data: new Float32Array([1, 2]),
|
|
1820
|
+
alignment: 1000000000n,
|
|
1821
|
+
alignmentMultiple: 1000000n,
|
|
1822
|
+
});
|
|
1823
|
+
const iter = series.subAlignmentIterator(1000500000n, 1001500000n);
|
|
1824
|
+
expect(iter.next().value).toBe(2);
|
|
1825
|
+
expect(iter.next().done).toBe(true);
|
|
1826
|
+
});
|
|
1827
|
+
|
|
1828
|
+
it("should handle edge case where start equals end with alignmentMultiple", () => {
|
|
1829
|
+
const series = new Series({
|
|
1830
|
+
data: new Float32Array([1, 2, 3]),
|
|
1831
|
+
alignment: 10n,
|
|
1832
|
+
alignmentMultiple: 5n,
|
|
1833
|
+
});
|
|
1834
|
+
const iter = series.subAlignmentIterator(15n, 15n);
|
|
1835
|
+
expect(iter.next().done).toBe(true);
|
|
1836
|
+
});
|
|
1837
|
+
|
|
1838
|
+
it("should work correctly when iterating exactly one sample with alignmentMultiple", () => {
|
|
1839
|
+
const series = new Series({
|
|
1840
|
+
data: new Float32Array([10, 20, 30]),
|
|
1841
|
+
alignment: 100n,
|
|
1842
|
+
alignmentMultiple: 10n,
|
|
1843
|
+
});
|
|
1844
|
+
const iter = series.subAlignmentIterator(110n, 120n);
|
|
1845
|
+
expect(iter.next().value).toBe(20);
|
|
1846
|
+
expect(iter.next().done).toBe(true);
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1849
|
+
it("should handle negative start indices correctly with alignmentMultiple", () => {
|
|
1850
|
+
const series = new Series({
|
|
1851
|
+
data: new Float32Array([1, 2, 3]),
|
|
1852
|
+
alignment: 50n,
|
|
1853
|
+
alignmentMultiple: 10n,
|
|
1854
|
+
});
|
|
1855
|
+
const iter = series.subAlignmentIterator(40n, 65n);
|
|
1856
|
+
expect(iter.next().value).toBe(1);
|
|
1857
|
+
expect(iter.next().value).toBe(2);
|
|
1858
|
+
expect(iter.next().done).toBe(true);
|
|
1859
|
+
});
|
|
1860
|
+
});
|
|
1320
1861
|
});
|
|
1321
1862
|
|
|
1322
1863
|
describe("parseJSON", () => {
|
package/src/telem/series.ts
CHANGED
|
@@ -66,6 +66,7 @@ interface BaseSeriesArgs {
|
|
|
66
66
|
sampleOffset?: math.Numeric;
|
|
67
67
|
glBufferUsage?: GLBufferUsage;
|
|
68
68
|
alignment?: bigint;
|
|
69
|
+
alignmentMultiple?: bigint;
|
|
69
70
|
key?: string;
|
|
70
71
|
}
|
|
71
72
|
|
|
@@ -165,6 +166,12 @@ export class Series<T extends TelemValue = TelemValue>
|
|
|
165
166
|
* group. Useful for defining the position of the series within a channel's data.
|
|
166
167
|
*/
|
|
167
168
|
readonly alignment: bigint = 0n;
|
|
169
|
+
/**
|
|
170
|
+
* Alignment multiple defines the number of alignment steps taken per sample. This is
|
|
171
|
+
* useful for when the samples in a series represent a partial view of the raw data
|
|
172
|
+
* i.e. decimation or averaging.
|
|
173
|
+
*/
|
|
174
|
+
readonly alignmentMultiple: bigint = 1n;
|
|
168
175
|
/** A cached minimum value. */
|
|
169
176
|
private cachedMin?: math.Numeric;
|
|
170
177
|
/** A cached maximum value. */
|
|
@@ -282,6 +289,7 @@ export class Series<T extends TelemValue = TelemValue>
|
|
|
282
289
|
sampleOffset = 0,
|
|
283
290
|
glBufferUsage = "static",
|
|
284
291
|
alignment = 0n,
|
|
292
|
+
alignmentMultiple = 1n,
|
|
285
293
|
key = id.create(),
|
|
286
294
|
data,
|
|
287
295
|
} = props;
|
|
@@ -294,6 +302,7 @@ export class Series<T extends TelemValue = TelemValue>
|
|
|
294
302
|
this._data = data_._data;
|
|
295
303
|
this.timeRange = data_.timeRange;
|
|
296
304
|
this.alignment = data_.alignment;
|
|
305
|
+
this.alignmentMultiple = data_.alignmentMultiple;
|
|
297
306
|
this.cachedMin = data_.cachedMin;
|
|
298
307
|
this.cachedMax = data_.cachedMax;
|
|
299
308
|
this.writePos = data_.writePos;
|
|
@@ -369,6 +378,7 @@ export class Series<T extends TelemValue = TelemValue>
|
|
|
369
378
|
|
|
370
379
|
this.key = key;
|
|
371
380
|
this.alignment = alignment;
|
|
381
|
+
this.alignmentMultiple = alignmentMultiple;
|
|
372
382
|
this.sampleOffset = sampleOffset ?? 0;
|
|
373
383
|
this.timeRange = timeRange ?? TimeRange.ZERO;
|
|
374
384
|
this.gl = {
|
|
@@ -685,7 +695,7 @@ export class Series<T extends TelemValue = TelemValue>
|
|
|
685
695
|
atAlignment(alignment: bigint, required?: false): T | undefined;
|
|
686
696
|
|
|
687
697
|
atAlignment(alignment: bigint, required?: boolean): T | undefined {
|
|
688
|
-
const index = Number(alignment - this.alignment);
|
|
698
|
+
const index = Number((alignment - this.alignment) / this.alignmentMultiple);
|
|
689
699
|
if (index < 0 || index >= this.length) {
|
|
690
700
|
if (required === true) throw new Error(`[series] - no value at index ${index}`);
|
|
691
701
|
return undefined;
|
|
@@ -873,7 +883,10 @@ export class Series<T extends TelemValue = TelemValue>
|
|
|
873
883
|
* is exclusive.
|
|
874
884
|
*/
|
|
875
885
|
get alignmentBounds(): bounds.Bounds<bigint> {
|
|
876
|
-
return bounds.construct(
|
|
886
|
+
return bounds.construct(
|
|
887
|
+
this.alignment,
|
|
888
|
+
this.alignment + BigInt(this.length) * this.alignmentMultiple,
|
|
889
|
+
);
|
|
877
890
|
}
|
|
878
891
|
|
|
879
892
|
private maybeGarbageCollectGLBuffer(gl: GLBufferController): void {
|
|
@@ -946,11 +959,13 @@ export class Series<T extends TelemValue = TelemValue>
|
|
|
946
959
|
* @returns An iterator over the specified alignment range.
|
|
947
960
|
*/
|
|
948
961
|
subAlignmentIterator(start: bigint, end: bigint): Iterator<T> {
|
|
949
|
-
|
|
950
|
-
this,
|
|
951
|
-
Number(start - this.alignment),
|
|
952
|
-
Number(end - this.alignment),
|
|
962
|
+
const startIdx = Math.ceil(
|
|
963
|
+
Number(start - this.alignment) / Number(this.alignmentMultiple),
|
|
953
964
|
);
|
|
965
|
+
const endIdx = Math.ceil(
|
|
966
|
+
Number(end - this.alignment) / Number(this.alignmentMultiple),
|
|
967
|
+
);
|
|
968
|
+
return new SubIterator(this, startIdx, endIdx);
|
|
954
969
|
}
|
|
955
970
|
|
|
956
971
|
private subBytes(start: number, end?: number): Series {
|
|
@@ -1041,7 +1056,7 @@ class SubIterator<T> implements Iterator<T> {
|
|
|
1041
1056
|
|
|
1042
1057
|
constructor(series: Series, start: number, end: number) {
|
|
1043
1058
|
this.series = series;
|
|
1044
|
-
const b = bounds.construct(0, series.length);
|
|
1059
|
+
const b = bounds.construct(0, series.length + 1);
|
|
1045
1060
|
this.end = bounds.clamp(b, end);
|
|
1046
1061
|
this.index = bounds.clamp(b, start);
|
|
1047
1062
|
}
|
|
@@ -1360,7 +1375,9 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
|
|
|
1360
1375
|
if (start < ser.alignment) break;
|
|
1361
1376
|
else if (start >= ser.alignmentBounds.upper) startIdx += ser.length;
|
|
1362
1377
|
else if (bounds.contains(ser.alignmentBounds, start)) {
|
|
1363
|
-
startIdx +=
|
|
1378
|
+
startIdx += Math.ceil(
|
|
1379
|
+
Number(start - ser.alignment) / Number(ser.alignmentMultiple),
|
|
1380
|
+
);
|
|
1364
1381
|
break;
|
|
1365
1382
|
}
|
|
1366
1383
|
}
|
|
@@ -1370,7 +1387,9 @@ export class MultiSeries<T extends TelemValue = TelemValue> implements Iterable<
|
|
|
1370
1387
|
if (end < ser.alignment) break;
|
|
1371
1388
|
else if (end >= ser.alignmentBounds.upper) endIdx += ser.length;
|
|
1372
1389
|
else if (bounds.contains(ser.alignmentBounds, end)) {
|
|
1373
|
-
endIdx +=
|
|
1390
|
+
endIdx += Math.ceil(
|
|
1391
|
+
Number(end - ser.alignment) / Number(ser.alignmentMultiple),
|
|
1392
|
+
);
|
|
1374
1393
|
break;
|
|
1375
1394
|
}
|
|
1376
1395
|
}
|