Skip to content

Commit

Permalink
chore: add recursive mul blueprints
Browse files Browse the repository at this point in the history
Signed-off-by: Sacha Froment <[email protected]>
  • Loading branch information
sfroment committed Feb 23, 2025
1 parent a5b24c7 commit f8dbf6f
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 2 deletions.
44 changes: 44 additions & 0 deletions packages/blueprints/src/RecMul/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
ActionType,
type DRP,
type ResolveConflictsType,
SemanticsType,
type Vertex,
} from "@ts-drp/object";

export class RecMulDRP implements DRP {
semanticsType = SemanticsType.pair;

private _value: number;

constructor(initialValue?: number) {
if (typeof initialValue === "number") {
this._value = initialValue;
} else {
this._value = 1;
}
}

recursive_mul(value: number): void {
if (typeof value !== "number") {
return;
}
if (value === 0) {
return;
}
this._value = this._multiply(value, this._value);
}

private _multiply(value: number, base: number): number {
if (value === 1) return base; // Base case
return base + this._multiply(value - 1, base);
}

query_value(): number {
return this._value;
}

resolveConflicts(_: Vertex[]): ResolveConflictsType {
return { action: ActionType.Nop };
}
}
1 change: 1 addition & 0 deletions packages/blueprints/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./AddMul/index.js";
export * from "./RecMul/index.js";
export * from "./Set/index.js";
export * from "./Map/index.js";
130 changes: 130 additions & 0 deletions packages/blueprints/tests/RecMul.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { ActionType, Vertex } from "@ts-drp/object";
import { beforeEach, describe, expect, test } from "vitest";

import { RecMulDRP } from "../src/RecMul/index.js";

describe("RecMulDRP tests", () => {
let drp: RecMulDRP;

beforeEach(() => {
drp = new RecMulDRP();
});

test("Test: recursive_mul (Basic)", () => {
drp.recursive_mul(3);
let val = drp.query_value();
expect(val).toEqual(3); // 1 * 3

drp.recursive_mul(4);
val = drp.query_value();
expect(val).toEqual(12); // 3 * 4

drp.recursive_mul(0);
expect(drp.query_value()).toEqual(12); // Should not change for 0
});

test("Test: recursive_mul (Type Safety)", () => {
drp.recursive_mul(2);
expect(drp.query_value()).toEqual(2); // 1 * 2

// @ts-expect-error Testing invalid input
drp.recursive_mul("");
expect(drp.query_value()).toEqual(2);

// @ts-expect-error Testing invalid input
drp.recursive_mul(true);
expect(drp.query_value()).toEqual(2);

// @ts-expect-error Testing invalid input
drp.recursive_mul({});
expect(drp.query_value()).toEqual(2);
});

test("Test: initialValue (Basic)", () => {
drp = new RecMulDRP(10);
expect(drp.query_value()).toEqual(10);

drp = new RecMulDRP(-10);
expect(drp.query_value()).toEqual(-10);

drp = new RecMulDRP(0);
expect(drp.query_value()).toEqual(0);

drp = new RecMulDRP();
expect(drp.query_value()).toEqual(1); // Default value is 1 for multiplication
});

test("Test: initialValue (Type Safety)", () => {
// @ts-expect-error Testing invalid input
drp = new RecMulDRP("10");
expect(drp.query_value()).toEqual(1);

// @ts-expect-error Testing invalid input
drp = new RecMulDRP(true);
expect(drp.query_value()).toEqual(1);

// @ts-expect-error Testing invalid input
drp = new RecMulDRP({});
expect(drp.query_value()).toEqual(1);

// @ts-expect-error Testing invalid input
drp = new RecMulDRP([]);
expect(drp.query_value()).toEqual(1);
});

test("Test: resolveConflicts (Basic)", () => {
const vertex1: Vertex = {
hash: "1",
peerId: "1",
operation: {
drpType: "DRP",
opType: "recursive_mul",
value: [3],
},
dependencies: [],
timestamp: 0,
signature: new Uint8Array(),
};
const vertex2: Vertex = {
hash: "2",
peerId: "2",
operation: {
drpType: "DRP",
opType: "recursive_mul",
value: [2],
},
dependencies: [],
timestamp: 0,
signature: new Uint8Array(),
};

let action = drp.resolveConflicts([]);
expect(action).toEqual({ action: ActionType.Nop });

action = drp.resolveConflicts([vertex1]);
expect(action).toEqual({ action: ActionType.Nop });

action = drp.resolveConflicts([vertex1, vertex2]);
expect(action).toEqual({ action: ActionType.Nop });
});

test("Test: resolveConflicts (Type Safety)", () => {
const vertex1: Vertex = {
hash: "1",
peerId: "1",
operation: {
drpType: "DRP",
opType: "recursive_mul",
value: [2],
},
dependencies: [],
timestamp: 0,
signature: new Uint8Array(),
};

const vertex2 = {};

let action = drp.resolveConflicts([vertex1, vertex2]);
expect(action).toEqual({ action: ActionType.Nop });
});
});
53 changes: 53 additions & 0 deletions packages/object/tests/actiontypes.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AddMulDRP } from "@ts-drp/blueprints/src/AddMul/index.js";
import { RecMulDRP } from "@ts-drp/blueprints/src/RecMul/index.js";
import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest";

import { DRPObject, ObjectACL } from "../src/index.js";
Expand Down Expand Up @@ -118,3 +119,55 @@ describe("Test: ActionTypes (Drops)", () => {

test("Test: Drop", () => {});
});

describe("Test: ActionTypes (RecMul)", () => {
let drp: DRPObject;
let drp2: DRPObject;
let recMul: RecMulDRP;
let recMul2: RecMulDRP;

beforeEach(() => {
drp = new DRPObject({ peerId: "peer1", drp: new RecMulDRP(), acl });
drp2 = new DRPObject({ peerId: "peer2", drp: new RecMulDRP(), acl });
recMul = drp.drp as RecMulDRP;
recMul2 = drp2.drp as RecMulDRP;

vi.useFakeTimers();
vi.setSystemTime(new Date(Date.UTC(1998, 11, 19)));
});

test("Test: Basic Operations", () => {
recMul.recursive_mul(3);
recMul2.recursive_mul(2);
console.log(recMul.query_value());
drp.merge(drp2.vertices);
drp2.merge(drp.vertices);
expect(recMul.query_value()).toBe(6); // 1*3*2
expect(recMul2.query_value()).toBe(6);

recMul.recursive_mul(2);
recMul2.recursive_mul(1);
drp.merge(drp2.vertices);
drp2.merge(drp.vertices);
expect(recMul.query_value()).toBe(12); // 6 * 2 * 1
expect(recMul2.query_value()).toBe(12);
});

test("Test: Multiple Operations", () => {
recMul.recursive_mul(3);
recMul.recursive_mul(3);
recMul2.recursive_mul(3);
drp.merge(drp2.vertices);
drp2.merge(drp.vertices);
expect(recMul.query_value()).toBe(27); // 1*3*3*3
expect(recMul2.query_value()).toBe(27);

recMul.recursive_mul(2);
recMul.recursive_mul(1);
recMul2.recursive_mul(2);
drp.merge(drp2.vertices);
drp2.merge(drp.vertices);
expect(recMul.query_value()).toBe(108); // 27 * 2 * 1 * 2
expect(recMul2.query_value()).toBe(108);
});
});
51 changes: 49 additions & 2 deletions packages/object/tests/hashgraph.bench.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { MapDRP } from "@ts-drp/blueprints/src/index.js";
import { SetDRP } from "@ts-drp/blueprints/src/index.js";
import { MapDRP, RecMulDRP, SetDRP } from "@ts-drp/blueprints/src/index.js";
import Benchmark from "benchmark";

import { DRP, DRPObject, ObjectACL } from "../src/index.js";
Expand Down Expand Up @@ -50,6 +49,40 @@ function benchmarkForAddWinSet(
}
});
}

function benchmarkForAddMulRecCall(
name: string,
numDRPs: number,
verticesPerDRP: number,
mergeFn: boolean
) {
return suite.add(name, () => {
const objects: DRPObject[] = [];
for (let i = 0; i < numDRPs; i++) {
const obj: DRPObject = new DRPObject({
peerId: `peer${i + 1}`,
acl,
drp: new RecMulDRP(),
});
const drp = obj.drp as RecMulDRP;
for (let j = 0; j < verticesPerDRP; j++) {
drp.recursive_mul(j);
}
objects.push(obj);
}

if (mergeFn) {
for (let i = 0; i < objects.length; i++) {
for (let j = 0; j < objects.length; j++) {
if (i !== j) {
objects[i].merge(objects[j].hashGraph.getAllVertices());
}
}
}
}
});
}

const suite = new Benchmark.Suite();

benchmarkForAddWinSet(
Expand All @@ -66,6 +99,20 @@ benchmarkForAddWinSet(
true
);

benchmarkForAddMulRecCall(
"Create a HashGraph with 1000 operations for rec mul",
1,
NUMBER_OF_OPERATIONS,
false
);

benchmarkForAddMulRecCall(
`Create 2 DRP Objects ${NUMBER_OF_OPERATIONS} vertices each and Merge for rec mul`,
2,
NUMBER_OF_OPERATIONS,
true
);

suite.add("Create a HashGraph with 1000 operations for set wins map", () => {
const object: DRPObject = new DRPObject({
peerId: "peer1",
Expand Down

0 comments on commit f8dbf6f

Please sign in to comment.