Use anonyous function in object for more than one task - javascript

I've got an Object "region" (will be different regions in the world), and I'm going to have an array of them with various properties that I apply.
I'd like the list of objects to contain the code to do various things like normalise the internal data, and add weights.
Here i have TTT (total travel time), TJL (total journey length) and PeaceIndex (how dangerous somewhere is) as properties.
Once I have the whole list I'm going to rank them against each other normalised to start with.
function region(TTT, TJL, peaceIndex) {
var n = function (TTTtopvalue,TTTbottomvalue,TTT) {
var colVal = parseFloat(TTT);
var numerator = colVal - TTTbottomvalue; //Once you have the extremes 33, 55, 56, 77 e.g. 33 and 77 then (value-min)/(max-min) 55-33=22/77-33= 22/50 = .4 whatever
var denominator = TTTtopvalue - TTTbottomvalue;
var result = numerator/denominator
this.TTTnormal =result ;
}
this.TTT = TTT;
this.TJL = TJL;
this.TTTnormal = 0;
this.TTTtopvalue = 0;
this.TTTbottomvalue = 0;
this.peaceIndex = peaceIndex;
this.normaliseTTT = n;
}
var r1 = new region(45, 33, 50);
var r2 = new region(40, 30, 55);
var r3 = new region(333, 100, 1);
var testArray = [r1, r2, r3];
console.log(JSON.stringify(testArray));
testArray[0].TTTtopvalue = 333;
testArray[0].TTTbottomvalue = 40;
testArray[0].normaliseTTT(333,40,45); //this works for TTT!!
console.log(JSON.stringify(testArray));
testArray.sort(function(a, b){
return a.TTT-b.TTT
})
console.log(JSON.stringify(testArray));
Now that works great for the TTT column. However it is the same code for the TJL and peaceIndex columns.
I just can't seem to get that anonymous function to return the normalised value to the other properties.
How can I do this?
So the prototype would be
function (topvalue,bottomvalue,TTT or TJL or peaceIndex)
Save typing the thing out each time

Separation of concerns is the answer. You need a separate class that represents a normalized value.
function NormalizedValue(value, top, bottom) {
this.init(value, top, bottom);
}
NormalizedValue.prototype.init = function (value, top, bottom) {
value = parseFloat(value);
top = parseFloat(top);
bottom = parseFloat(bottom);
this.value = value;
this.normalized = (value - bottom) / (top - bottom);
}
then
function Region(name) {
this.name = name;
this.TTT = new NormalizedValue();
this.TJL = new NormalizedValue();
this.peaceIndex = new NormalizedValue();
}
var r1 = new Region("A");
var r2 = new Region("B");
var r3 = new Region("C");
r1.TTT.init(333, 40, 45);
r1.TJL.init(40, 30, 25);
r1.peaceIndex.init(1, 5, 1);
// and so on for the others...
and then, for example
testArray.sort(function (a, b) {
return a.TTT.normalized - b.TTT.normalized;
});
You can structure your Region constructor differently so that more of the init values can be passed as arguments, but take care that it does not get too messy (a 10-argument constructor isn't a pretty thing).

Related

Point Doubling (P -> 2P) The Base Point of the Secp256k1 Elliptic Curve

as a learning exercise, I am trying to code the first point doubling (Base point P -> 2P) for the Secp256k1 Elliptic Curve. I am using Javascript, and the ethers package for BigNumber. Frustratingly, I am running into a problem where the result I am getting for 2P doesn't appear to lie on the curve. Can someone please help me determine where I am making a mistake?
The coordinates I'm getting as a result are:
X: 0xf1b9e9c77c87bf0ac622382b581826898cfc9232e025d86d904bfd33375faf1a
Y: 0x8162c7b446b54638e9181b71770b2d718e6953a360625a02392097c7db09c608
Which returns false from my isPointOnCurve() method. As a sanity check, I checked the base point in the isPointOnCurve() method, and that returns true (thankfully).
Please see my code below:
const { ethers, BigNumber } = require('ethers');
//variable initialization found from https://en.bitcoin.it/wiki/Secp256k1
bigZero = BigNumber.from(0);
bigTwo = BigNumber.from(2);
bigThree = BigNumber.from(3);
ellipticCurveB = BigNumber.from(7);
generatorPrime = BigNumber.from("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
order = BigNumber.from("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
baseXCoord = BigNumber.from("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798");
baseYCoord = BigNumber.from("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
// slope = ( (3*x^2) * (2*y)^-1 ) mod order
// 2Px = slope^2 - 2*baseXCoord
// 2Py = slope * ( 2Px - baseXCoord ) - baseYCoord
m = (bigThree.mul(baseXCoord.pow(bigTwo)).mul(modinv(bigTwo.mul(baseYCoord), order))).mod(order);
TwoPx = (m.pow(bigTwo).sub(bigTwo.mul(baseXCoord))).mod(order);
TwoPy = ((m.mul(baseXCoord.sub(TwoPx))).sub(baseYCoord)).mod(order);
console.log(TwoPx);
console.log(TwoPy);
console.log(isPointOnCurve(TwoPx, TwoPy));
// Helper Functions:
// Check if point is on Curve, Calculate extended GCD, modular inverse
function isPointOnCurve(x,y){
b = ellipticCurveB;
p = generatorPrime;
rem = (y.pow(bigTwo).sub(x.pow(bigThree)).sub(b)).mod(p);
return rem.eq(bigZero);
}
function egcd(a, b) {
var s = BigNumber.from(0), t = BigNumber.from(1), u = BigNumber.from(1), v = BigNumber.from(0);
while (!a.eq(BigNumber.from(0))) {
var q = b.div(a) | BigNumber.from(0), r = b.mod(a);
var m = s.sub(u.mul(q)), n = t.sub(v.mul(q));
b = a;
a = r;
s = u;
t = v;
u = m;
v = n;
}
return [b, s, t];
}
function mod(x, y) {
return (x.mod(y).add(y)).mod(y);
}
function modinv(x, y) {
var tuple = egcd(x.mod(y), y);
if (!tuple[0].eq(BigNumber.from(1))) {
return null;
}
return mod(tuple[1], y);
}
As kelalaka pointed out in a comment on the original post, I was confusing the the order of the group and the finite field Fp. I was getting values modulo the Group Order, when I should've been using the values modulo prime p used to define the finite field.
The new and correct result I get is:
X: 0xc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5
Y: 0x1ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a
If anyone would like to use this code, I've updated it to be correct, and cleaned it up to make it a little more readable:
bigZero = BigNumber.from(0);
bigTwo = BigNumber.from(2);
bigThree = BigNumber.from(3);
ellipticCurveB = BigNumber.from(7);
generatorPrime = BigNumber.from("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
baseXCoord = BigNumber.from("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798");
baseYCoord = BigNumber.from("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");
// slope = ( (3*x^2) * (2*y)^-1 ) mod order
threeXSquared = bigThree.mul(baseXCoord.pow(bigTwo));
modInv2y = modinv(bigTwo.mul(baseYCoord), generatorPrime);
m = threeXSquared.mul(modInv2y).mod(generatorPrime);
// 2Px = slope^2 - 2*baseXCoord
mSquared = m.pow(bigTwo);
twoXbase = bigTwo.mul(baseXCoord);
TwoPx = (mSquared.sub(twoXbase)).mod(generatorPrime);
// 2Py = slope * ( 2Px - baseXCoord ) - baseYCoord
pointSlopeX = m.mul(baseXCoord.sub(TwoPx));
TwoPy = (pointSlopeX).sub(baseYCoord).mod(generatorPrime);
console.log(TwoPx);
console.log(TwoPy);
console.log(isPointOnCurve(TwoPx, TwoPy));
// Helper Functions:
// Check if point is on Curve, Calculate extended GCD, modular inverse
function isPointOnCurve(x,y){
b = ellipticCurveB;
p = generatorPrime;
rem = (y.pow(bigTwo).sub(x.pow(bigThree)).sub(b)).mod(p);
return rem.eq(bigZero);
}
function egcd(a, b) {
var s = BigNumber.from(0), t = BigNumber.from(1), u = BigNumber.from(1), v = BigNumber.from(0);
while (!a.eq(BigNumber.from(0))) {
var q = b.div(a) | BigNumber.from(0), r = b.mod(a);
var m = s.sub(u.mul(q)), n = t.sub(v.mul(q));
b = a;
a = r;
s = u;
t = v;
u = m;
v = n;
}
return [b, s, t];
}
function modulus(x, y) {
return (x.mod(y).add(y)).mod(y);
}
function modinv(x, y) {
var tuple = egcd(x.mod(y), y);
if (!tuple[0].eq(BigNumber.from(1))) {
return null;
}
return modulus(tuple[1], y);
}

JS Console Variable Output Issue

I am having an issue running a bit of javascript I have made (converted from existing Google python code) to define a zoom level based on latitude and longitude values of a rectangle in a google map. I am currently having an issue with the output of a couple variables.. I have attached an image showing the variables in question and their outputs using the console.log() command.
As you can see the bottom_left and top_right variables differ from their assignments bounds[0] and bounds[1] respectively. I'm sure that I am doing something wrong here in this function, as the output is different from what I mean to assign the variables as. I was also wondering why there is a problem when using console.log(bottom_left) or console.log(top_right) in the console? Is it because these variables aren't globally defined?
Overall, the code fails to run properly as it outputs the maximum Zoom no matter what Lat / Long values are inputted (Theoretically the zoom level should get smaller and smaller with the increase in Lat / Long extents).
Below is the entire code from the sample:
//Define initial variables
var southWestLat = 10;
var southWestLng = -180;
var northEastLat = 60;
var northEastLng = -50;
var bounds = new Array ();
bounds[0] = new Array (southWestLat,southWestLng);
bounds[1] = new Array (northEastLat,northEastLng)
//------------------------------------
//------------------------------------
//------------------------------------
//Point constructor
function Point(x, y) {
this.x = x;
this.y = y;
}
//------------------------------------
//------------------------------------
//------------------------------------
function CalcWrapWidth(zoom) {
return pixel_range[zoom]
}
//------------------------------------
//------------------------------------
//------------------------------------
function range(lowEnd,highEnd){
var arr = [],
c = highEnd - lowEnd + 1;
while ( c-- ) {
arr[c] = highEnd--
}
return arr;
}
//------------------------------------
//------------------------------------
//------------------------------------
function Bound(value,opt_min,opt_max) {
if (opt_min != null) {
value = Math.max(value,opt_min);
}
if (opt_max != null) {
value = Math.min(value,opt_max);
}
return value;
}
//------------------------------------
//------------------------------------
//------------------------------------
//Converts from degrees to radians
function DegToRad(deg) {
return deg*(Math.pi/180);
}
//------------------------------------
//------------------------------------
//------------------------------------
//Gets center bounds, bounds as ['lat','lng']
function GetCenter(bounds) {
var center_lat = (bounds[1][0] + bounds[0][0])/2;
var center_lng = (bounds[0][1] + bounds[1][1])/2;
var center = new Array ();
center[0] = center_lat;
center[1] = center_lng;
return center;
}
//------------------------------------
//------------------------------------
//------------------------------------
//Prepare the calculation...
var pixels_per_lon_deg = new Array ();
var pixels_per_lon_rad = new Array ();
var pixel_origo = new Array ();
var pixel_range = new Array ();
var pixels = 640;
var zoom_levels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18];
var pointObj = new Array ();
var origin;
function prime() {
for (i in zoom_levels) {
origin = pixels/2;
pixels_per_lon_deg.push(pixels/360);
pixels_per_lon_rad.push(pixels/(2*Math.pi));
pixel_origo.push({x:origin,y:origin});
pixel_range.push(pixels);
pixels = pixels*2;
}
}
//------------------------------------
//------------------------------------
//------------------------------------
//Convert from Lat Lng to pixel
function FromLatLngToPixel(lat_lng, zoom) {
o=pixel_origo[zoom];
x_cord=Math.round(o.x+lat_lng[1]*pixels_per_lon_deg[zoom]);
siny=Bound(Math.sin(DegToRad(lat_lng[0])),-0.9999,0.9999);
y_cord=Math.round(o.y+0.5*Math.log((1+siny) / (1-siny))*-pixels_per_lon_rad[zoom]);
pointObj = ({x:x_cord,y:y_cord}); //Potential error here?
return pointObj
}
//------------------------------------
//------------------------------------
//------------------------------------
/**Main function bounds: A list of length 2, each holding a list of length 2. It holds
the southwest and northeast lat/lng bounds of a map. It should look
like this: [[southwestLat, southwestLng], [northeastLat, northeastLng]]**/
function CalculateBoundsZoomLevel(bounds) {
var zmax=18;
var zmin=0;
var bottom_left=bounds[0];
var top_right=bounds[1];
var backwards_range=range(zmin,zmax).reverse();
var lng_dsc = Math.abs(bounds[0][1] - bounds[1][1]);
var lat_dsc = Math.abs(bounds[0][0] - bounds[1][0]);
var rrun = lat_dsc/lng_dsc;
var runr = lng_dsc/lat_dsc;
var vs_height;
var vs_length;
console.log(bottom_left) //Delete
console.log(top_right) //Delete
if (rrun<1) {
vs_height = 640*rrun;
vs_length = 640;
} else {
vs_height = 640;
vs_length = 640*runr;
}
var view_size = new Array (vs_length,vs_height);
for (z in backwards_range) {
var bottom_left_pixel=FromLatLngToPixel(bottom_left,z);
var top_right_pixel=FromLatLngToPixel(top_right,z);
if (bottom_left_pixel.x > top_right_pixel.x) {
bottom_left_pixel.x -= CalcWrapWidth(z);
}
if (Math.abs(top_right_pixel.x - bottom_left_pixel.x) <= view_size[0] && Math.abs(top_right_pixel.y - bottom_left_pixel.y) <= view_size[1]) {
return z
}
}
return 0
}
//------------------------------------
//------------------------------------
//------------------------------------
//Run function
prime()
CalculateBoundsZoomLevel([southWestLat,southWestLng],[northEastLat,northEastLng])
console.log(z)
As always any help is greatly appreciated. Thanks!
Simple you are passing in two arguments to a function that expects one.
function CalculateBoundsZoomLevel(bounds) {
^^^^^^
CalculateBoundsZoomLevel([southWestLat,southWestLng],[northEastLat,northEastLng])
^^^^^^^^^^^^^^^^^^^^^^^^^^^
I assume you wanted it to be a 2D array
CalculateBoundsZoomLevel([[southWestLat,southWestLng],[northEastLat,northEastLng]])
You have the function defined as:
function CalculateBoundsZoomLevel(bounds) {
So inside the function, the variable bounds is the first argument from the call
CalculateBoundsZoomLevel([southWestLat,southWestLng],[northEastLat,northEastLng])
So bounds[0] == southWestLat and bounds[1] == southWestLng. This isn't the same as when you use console.log(bounds[0]) in the console. Then it's using the global variables that were defined with:
var bounds = new Array ();
bounds[0] = new Array (southWestLat,southWestLng);
bounds[1] = new Array (northEastLat,northEastLng)
The global bounds array is a 2-dimensional array, but inside the function it's just a 1-dimensional array.
You should use:
CalculateBoundsZoomLevel(bounds)
to use the same array inside the function as outside.

Array(push - object) not working as expected

I'm having issues with pushing object into an array.
I set an object with values and push them to the array. I then change some of the values of the object and push the object into the array again.
However, on inspection, both objects pushed into the array are identical, both object's values are identical to the last object that was pushed into the array.
let ProductPosition = function (x, y) {
this.x = x;
this.y = y;
}
let PalletType = (function () {
function PalletType() {
this.PatternType = '';
this.ProductWidth = 0;
this.PalletWidth = 0;
this.ProductPositions = [];
}
});
function getPalletPositions(pallet, pattern) {
pal.ProductPositions = [];
let posn = new ProductPosition();
switch (pattern) {
case '1U1':
posn = [];
posn.y = pal.PalletWidth / 2;
posn.angle = 0;
posn.apprDir = 0;
pallet.ProductPositions.push(posn);
break;
case '2U1':
posn = [];
posn.y = pal.PalletWidth / 2 + pal.ProductWidth / 2;
console.log('y pos 0 ' + posn.y);
pal.ProductPositions.push(posn);//first push
posn.y = pal.PalletWidth / 2 - pal.ProductWidth / 2;
console.log('y pos 1 ' + posn.y);
pallet.ProductPositions.push(posn);//first push
break;
}
}
let pal = new PalletType();
pal.PalletWidth = 1165;
pal.ProductWidth = 400
let pat = '2U1';
getPalletPositions(pal, pat);
pal.ProductPositions.forEach(function (pos) {
console.log("pos.y:" + pos.y);
});
Actual output:
y pos 0 782.5 <-value of y of first push
y pos 1 382.5 <-value of y of second push
pos.y:382.5 <-should be 782.5
pos.y:382.5
I'd expect:
y pos 0 782.5 <-value of y of first push
y pos 1 382.5 <-value of y of second push
pos.y:782.5
pos.y:382.5
I'm totally baffled and tried a few things, but to no avail.
You were mutating that object you can use spread operator or Object.assign
Check below
let ProductPosition = function (x, y) {
this.x = x;
this.y = y;
}
let PalletType = (function () {
function PalletType() {
this.PatternType = '';
this.ProductWidth = 0;
this.PalletWidth = 0;
this.ProductPositions = [];
}
});
function getPalletPositions(pallet, pattern) {
pal.ProductPositions = [];
let posn = new ProductPosition();
debugger;
switch (pattern) {
case '1U1':
posn = [];
posn.y = pal.PalletWidth / 2;
posn.angle = 0;
posn.apprDir = 0;
pallet.ProductPositions.push(posn);
break;
case '2U1':
posn = [];
posn.y = pal.PalletWidth / 2 + pal.ProductWidth / 2;
console.log('y pos 0 ' + posn.y);
pal.ProductPositions.push({...posn});//first push
posn.y = pal.PalletWidth / 2 - pal.ProductWidth / 2;
console.log('y pos 1 ' + posn.y);
pallet.ProductPositions.push({...posn});//first push
break;
}
}
let pal = new PalletType();
pal.PalletWidth = 1165;
pal.ProductWidth = 400
let pat = '2U1';
getPalletPositions(pal, pat);
pal.ProductPositions.forEach(function (pos) {
console.log("pos.y:" + pos.y);
});
That's because "posn" is an Object, so you are actually pushing a reference to this object rather than a primitive value.
You could, for example, copy the object:
pallet.ProductPositions.push({...posn});
The Spread operator will create a shallow copy.
If you need a deep copy use the following:
pallet.ProductPositions.push(JSON.Parse(JSON.Stringify(posn)));
Pay attention that the JSON method is not able to copy functions.
You're trying to push the same object 2 times in the same array. The first time with some value and next time, you're modifying the value in the same object and pushing in the array. So in total the same object reference is getting modified. As a result, the array has same object added 2 times.
Another way is, you can use slice operator on your array to create new instance of the array and then do the second push. OR create 2 different variables and then push it.
Thank you all for your prompt responses, it is very much appreciated.
Solved by using:
let x;
let y;
let angle;
let apprDir;
and assigning these directly. Then:
pallet.ProductPositions.push(new ProductPosition(x, y, angle, apprDir));
Works a treat and simplified the code.

How to get size of a DataView type (eg. Uint32=4, Float64=8) to advance the offset?

I'm parsing a serialized object with a DataView and want to be able to increment an offset variable depending on data sizes. I'd rather not redefine variables for simple things like BYTES_PER_UINT16
...
var dv = new DataView(payload);
var offset = 0;
anObject.field1 = dv.getUint8(offset);
offset += BYTES_PER_UINT8;
anObject.field2 = dv.getUint32(offset, true);
offset += BYTES_PER_UINT32;
...
You need to wrap them in an object which does this for you.
For example:
function DataViewExt(o) {
this.view = new DataView(o);
this.pos = 0;
}
DataViewExt.prototype = {
getUint8: function() {
return this.view.getUint8(this.pos++);
},
getUint16: function() {
var v = this.view.getUint16(this.pos);
this.pos += 2;
return v
},
// etc...
};
Now you can create an instance:
var dv = new DataViewExt(payload);
var uint8 = dv.getUint8(); // advances 1 byte
var uint16 = dv.getUint16(); // advances 2 bytes
...
console.log("Current position:", dv.pos);
Modify to fit your scenario.

Value not getting added to array?

Simple question which seems impossible for me because I'm just staring in the code.
Basicly I have this function, I call it X amount of times and it should put all the created divs in a array called world which I've declared outside of the function.
However, if I try to use one of these values they are "undefined".
var world = [];
function newTile(x, y, size, rotX, rotY, rotZ, tranX, tranY, tranZ, color) {
var tile = document.createElement('div');
tile.className = "tile";
tile.style.width = size+"px";
tile.style.height = size+"px";
tile.style.webkitTransform =
"rotateX("+rotX+"deg)"+
"rotateY("+rotY+"deg)"+
"rotateZ("+rotZ+"deg)"+
"translateX("+tranX+"px)"+
"translateY("+tranY+"px)"+
"translateZ("+tranZ+"px)";
tile.style.transform =
"rotateX("+rotX+"deg)"+
"rotateY("+rotY+"deg)"+
"rotateZ("+rotZ+"deg)"+
"translateX("+tranX+"px)"+
"translateY("+tranY+"px)"+
"translateZ("+tranZ+"px)";
if (x == 0 && y == 0) {
color="rgba(255,255,0,0.5)";
pathStart = tile;
pathCur = tile;
}
tile.style.backgroundColor = color;
tile.data = {
x:x,
y:y,
blacklist:0
}
tile.onclick = function() {
worldOri(null,null,null, -this.data.x*128 - 64, null, -this.data.y*128 - 64);
};
if (debug) tile.textContent = x+", "+y;
document.getElementById('world').appendChild(tile);
world[x] = [];
world[x][y] = tile;
}
Lets say I do something like:
newTile(2,6,128,90,0,0,2*128,0,6*128, "rgba(255,125,0,0.5)");
This works as intended and surely creates a div, placing it "in" another div with the id "world" and SHOULD add the div to the array "world" at [2][6]. If I now try to do something with the div, for example change color:
world[2][6].style.backgroundColor = "rgba(255,0,0,0.5)";
It returns as undefined, which I assume is that the actual adding to the "world" array didn't work, please help.
world[x] = []; will assign an empty array world[x] every time you make a call to newTile, thus "removing" all existing tiles from world[x]. Only initialize it if it doesn't exist yet:
world[x] = world[x] || [];

Categories