diff --git a/offchain/tests/admin/pellets/create-pellets.ts b/offchain/tests/admin/pellets/create-pellets.ts index 0dc0669..ac63d1b 100644 --- a/offchain/tests/admin/pellets/create-pellets.ts +++ b/offchain/tests/admin/pellets/create-pellets.ts @@ -3,6 +3,6 @@ import { createPellets } from "../../../transactions/admin/pellets/create-pellet import { readPelletsCSV } from "./utils.ts"; import { printTxURL } from "../../../utils.ts"; -const params = await readPelletsCSV("tests/admin/pellets.csv"); +const params = await readPelletsCSV("tests/admin/pellets/pellets.csv"); const txHash = await createPellets(admin_token, params); printTxURL(txHash); diff --git a/offchain/tests/admin/pellets/sample-pellets.ts b/offchain/tests/admin/pellets/sample-pellets.ts index bca9085..70487d9 100644 --- a/offchain/tests/admin/pellets/sample-pellets.ts +++ b/offchain/tests/admin/pellets/sample-pellets.ts @@ -1,4 +1,10 @@ -import { getDiamondAreaSample, writePelletsCSV } from "./utils.ts"; +import { + getRingAreaSample, + getDiamondAreaSample, + writePelletsCSV, +} from "./utils.ts"; -const pellets = getDiamondAreaSample(20n, 30n, 0.15); -writePelletsCSV(pellets, "tests/admin/pellets/sample1.csv"); +const pellets = getRingAreaSample(20, 30, 30n, 80n, 0.15); +//const pellets = getDiamondAreaSample(0n, 9n, 30n, 50n, 0.05); + +writePelletsCSV(pellets, "tests/admin/pellets/circle1.csv"); diff --git a/offchain/tests/admin/pellets/utils.ts b/offchain/tests/admin/pellets/utils.ts index 86aa62f..c91ecbd 100644 --- a/offchain/tests/admin/pellets/utils.ts +++ b/offchain/tests/admin/pellets/utils.ts @@ -1,5 +1,8 @@ import { parse, stringify } from "jsr:@std/csv"; +type Coordinates = Array<{ pos_x: bigint; pos_y: bigint }>; +type PelletParams = Array<{ fuel: number; pos_x: bigint; pos_y: bigint }>; + function getRandomSubarray(arr: Array, size: number) { const shuffled = arr.slice(0); let i = arr.length, @@ -20,83 +23,147 @@ function getRandomSubarray(arr: Array, size: number) { * actual "circumferences" (set of points equidistant from the center) in our geometry. * @param r Diamond diagonal. */ -function getDiamondCoordinates( - r: bigint -): Array<{ pos_x: bigint; pos_y: bigint }> { - const cs = []; +function getDiamondCoordinates(r: bigint): Coordinates { + const coordinates = []; for (let i = 0n; i < r; i++) { - cs.push({ + coordinates.push({ pos_x: r - i, pos_y: i, }); - cs.push({ + coordinates.push({ pos_x: -r + i, pos_y: -i, }); - cs.push({ + coordinates.push({ pos_x: -i, pos_y: r - i, }); - cs.push({ + coordinates.push({ pos_x: i, pos_y: -r + i, }); } - return cs; + return coordinates; } /** - * Returns an array with a random sample of pellet parameters on the perimeter - * of a diamond with diagonal r. - * @param r Diamond diagonal. - * @param density Density of the sample: equals 1 if every diamond point is taken. Must be in the range 0 - 1, inclusive. + * Returns an array with the coordinates of the points that lie in the + * area between two diamonds with diagonals inner_r and outer_r respectively. + * @param inner_r Inner diamond diagonal. Must be greater than or equal to 0. + * @param outer_r Outer diamond diagonal. Must be greater than or equal to inner_r. */ -function getDiamondSample( - r: bigint, - density: number -): Array<{ fuel: number; pos_x: bigint; pos_y: bigint }> { - if (density > 1 || density < 0) { - throw Error("Density must be a number between 0 and 1."); +function getDiamondAreaCoordinates( + inner_r: bigint, + outer_r: bigint +): Coordinates { + if (inner_r < 0 || inner_r > outer_r) { + throw Error( + "inner_r must be a positive number less than or equal to outer_r" + ); } - const cs = getDiamondCoordinates(r); - const sample_size = Math.floor(cs.length * density); - const sample_cs = getRandomSubarray(cs, sample_size); - const pellets = sample_cs.map((c) => ({ - fuel: Math.floor(Math.random() * 60 + 30), //random amount between 30 and 90. - pos_x: c.pos_x, - pos_y: c.pos_y, - })); - return pellets; + const coordinates = []; + for (let r = inner_r; r <= outer_r; r++) { + coordinates.push(getDiamondCoordinates(r)); + } + return coordinates.flat(); } /** * Returns an array with a random sample of pellet parameters over the area * between two diamonds with diagonals inner_r and outer_r respectively. * @param inner_r Inner diamond diagonal. Must be greater than or equal to 0. - * @param Outer_r Outer diamond diagonal. Must be greater than or equal to inner_r. + * @param outer_r Outer diamond diagonal. Must be greater than or equal to inner_r. + * @param min_fuel Minimum fuel held by the sample pellets. Must be greater than or equal to 0. + * @param max_fuel Maximum fuel held by the sample pellets. Must be greater than or equal to min_fuel. * @param density Density of the sample: equals 1 if every diamond point is taken. Must be in the range 0 - 1, inclusive. */ function getDiamondAreaSample( inner_r: bigint, outer_r: bigint, + min_fuel: bigint, + max_fuel: bigint, density: number -): Array<{ fuel: number; pos_x: bigint; pos_y: bigint }> { +): PelletParams { + if (density > 1 || density < 0) { + throw Error("Density must be a number between 0 and 1."); + } + if (min_fuel < 0 || min_fuel > max_fuel) { + throw Error( + "min_fuel must be a positive number less than or equal to max_fuel" + ); + } + const coordinates = getDiamondAreaCoordinates(inner_r, outer_r); + const sample_size = Math.floor(coordinates.length * density); + const sample_coordinates = getRandomSubarray(coordinates, sample_size); + const pellets = sample_coordinates.map((c) => ({ + fuel: Math.floor( + Math.random() * Number(max_fuel - min_fuel) + Number(min_fuel) + ), + pos_x: c.pos_x, + pos_y: c.pos_y, + })); + return pellets; +} + +/** + * Returns an array with the coordinates of the points that lie in the + * area between two circles with radii inner_r and outer_r respectively. + * @param inner_r Inner circle radius. Must be greater than or equal to 0. + * @param outer_r Outer circle radius. Must be greater than or equal to inner_r. + */ +function getRingAreaCoordinates(inner_r: number, outer_r: number): Coordinates { if (inner_r < 0 || inner_r > outer_r) { throw Error( "inner_r must be a positive number less than or equal to outer_r" ); } - const pellets = []; - for (let r = inner_r; r <= outer_r; r++) { - pellets.push(getDiamondSample(r, density)); - } - return pellets.flat(); + const x_bound = Math.floor(outer_r); + const xs = Array.from({ length: 2 * x_bound + 1 }, (_, i) => i - x_bound); + const coordinates = xs.map((x) => { + const y_outer_bound = Math.floor(Math.sqrt(outer_r ** 2 - x ** 2)); + let ys = Array.from( + { length: 2 * y_outer_bound + 1 }, + (_, i) => i - y_outer_bound + ); + if (Math.abs(x) < Math.abs(inner_r)) { + const y_inner_bound = Math.floor(Math.sqrt(inner_r ** 2 - x ** 2)); + ys = ys.filter((y) => Math.abs(y) > y_inner_bound); + } + return ys.map((y) => ({ pos_x: BigInt(x), pos_y: BigInt(y) })); + }); + return coordinates.flat(); +} + +/** + * Returns an array with a random sample of pellet parameters over the area + * between two circles with radii inner_r and outer_r respectively. + * @param inner_r Inner diamond diagonal. Must be greater than or equal to 0. + * @param outer_r Outer diamond diagonal. Must be greater than or equal to inner_r. + * @param min_fuel Minimum fuel held by the sample pellets. Must be greater than or equal to 0. + * @param max_fuel Maximum fuel held by the sample pellets. Must be greater than or equal to min_fuel. + * @param density Density of the sample: equals 1 if every diamond point is taken. Must be in the range 0 - 1, inclusive. + */ +function getRingAreaSample( + inner_r: number, + outer_r: number, + min_fuel: bigint, + max_fuel: bigint, + density: number +): PelletParams { + const coordinates = getRingAreaCoordinates(inner_r, outer_r); + const sample_size = Math.floor(coordinates.length * density); + const sample_coordinates = getRandomSubarray(coordinates, sample_size); + const pellets = sample_coordinates.map((c) => ({ + fuel: Math.floor( + Math.random() * Number(max_fuel - min_fuel) + Number(min_fuel) + ), + pos_x: c.pos_x, + pos_y: c.pos_y, + })); + return pellets; } -function writePelletsCSV( - pellets: Array<{ fuel: number; pos_x: bigint; pos_y: bigint }>, - path: string -) { +function writePelletsCSV(pellets: PelletParams, path: string) { const csv = stringify(pellets, { columns: ["fuel", "pos_x", "pos_y"], }); @@ -119,4 +186,9 @@ async function readPelletsCSV(path: string) { return params; } -export { getDiamondAreaSample, writePelletsCSV, readPelletsCSV }; +export { + getDiamondAreaSample, + getRingAreaSample, + writePelletsCSV, + readPelletsCSV, +};