<commit_before>var redis = require("redis"),
    client = redis.createClient();
var async = require('async');
var config = require('./config.js').config();

// Pearson product-moment correlation coefficient

var pearsonCoefficient = function(users, person1, person2){
  var commonMovies = {};
  _.each(users[person1], function(value, key){
    if(users[person2].hasOwnProperty(key)){
      commonMovies[key]=1;
    }
  });
  var n = _.size(commonMovies);
  if (n === 0){
    return 0;
  }
  // add up all preferences
  var sum1 = _.reduce(commonMovies, function(memo, value, key){
    return memo + users[person1][key];
  }, 0);
  var sum2 = _.reduce(commonMovies, function(memo, value, key){
    return memo + users[person2][key];
  }, 0);
  // sum up all squares
  var sum1Sq = _.reduce(commonMovies, function(memo, value, key){
    return memo + Math.pow(users[person1][key],2);
  }, 0);
  var sum2Sq = _.reduce(commonMovies, function(memo, value, key){
    return memo + Math.pow(users[person2][key],2);
  }, 0);
  // sum up the products of all the matches
  var pSum = _.reduce(commonMovies, function(memo, value, key){
    return memo + (users[person1][key]*users[person2][key]);
  }, 0);
  // calculate the pearson score
  var num = pSum - (sum1*sum2/n);
  var den = Math.sqrt((sum1Sq-Math.pow(sum1,2)/n)*(sum2Sq-Math.pow(sum2,2)/n));

  if (den === 0 ){
    return 0;
  }
  var r = num/den;
  return r;
};

var jaccardCoefficient = function(userId1, userId2, callback){
  var similarity = 0,
  likedCount = 0,
  dislikedCount = 0,
  finalJaccard = 0;
  client.sinter([config.className,userId1,'liked'].join(":"),[config.className,userId2,'liked'].join(":"), function(err, results1){
    client.sinter([config.className,userId1,'disliked'].join(":"),[config.className,userId2,'disliked'].join(":"), function(err, results2){
      client.sinter([config.className,userId1,'liked'].join(":"),[config.className,userId2,'disliked'].join(":"), function(err, results3){
        client.sinter([config.className,userId1,'disliked'].join(":"),[config.className,userId2,'liked'].join(":"), function(err, results4){
          client.scard([config.className,userId1,'liked'].join(":"), function(err, likedCount){
            client.scard([config.className,userId1,'disliked'].join(":"), function(err, dislikedCount){
              similarity += (results1.length+results2.length-results3.length-results4.length);
              finalJaccardScore = similarity / (likedCount + dislikedCount);
              callback(finalJaccardScore);
            });
          });
        });
      });
    });
  });
};

exports.updateSimilarityFor = function(userId, callback){
  userId = String(userId);
  var similaritySet, userRatedMovieIds, movieLikedByUsers, movieDislikedByUsers, similarUserIds;
  similaritySet = [config.className,userId,'similaritySet'].join(":");
  client.sunion([config.className,userId,'liked'].join(":"),[config.className,userId,'disliked'].join(":"), function(err, userRatedMovieIds){
    if (userRatedMovieIds.length > 0){
      similarUserIds = _.map(userRatedMovieIds, function(otherUserRatedId, key){
        movieLikedByUsers = [config.className, otherUserRatedId, 'liked'].join(":");
        movieDislikedByUsers = [config.className, otherUserRatedId, 'disliked'].join(":");
        return [movieLikedByUsers, movieDislikedByUsers];
      });
    }
    similarUserIds = _.flatten(similarUserIds);
    client.sunion(similarUserIds, function(err, results){
      _.each(results, function(otherUserId, key){
        if (userId !== otherUserId){
          jaccardCoefficient(userId, otherUserId, function(result) {
            client.zadd(similaritySet, result, otherUserId, function(err){
              callback();
            });
          });
        }
      });
    });
  });
};

exports.predictFor = function(userId, movieId, callback){
  userId = String(userId);
  movieId = String(movieId);
  var finalSimilaritySum = 0.0;
  var prediction = 0.0;
  var similaritySet = [config.className, userId, 'similaritySet'].join(':');
  var likedBySet = [config.className, movieId, 'liked'].join(':');
  var dislikedBySet = [config.className, movieId, 'disliked'].join(':');
  exports.similaritySum(similaritySet, likedBySet, function(result1){
    exports.similaritySum(similaritySet, dislikedBySet, function(result2){
      finalSimilaritySum = result1 - result2;
      client.scard(likedBySet, function(err, likedByCount){
        client.scard(dislikedBySet, function(err, dislikedByCount){
          prediction = finalSimilaritySum / parseFloat(likedByCount + dislikedByCount);
          if (isFinite(prediction)){
            console.log('prediction', userId, prediction);
            callback(prediction);
          }
        });
      });
    });
  });
  // callback(1);
};

exports.similaritySum = function(simSet, compSet, cb){ // trying to get the score from the similarity set given the userId from the comp set
  var similarSum = 0.0;
  client.smembers(compSet, function(err, userIds){
    async.each(userIds,
      function(userId, callback){
        client.zscore(simSet, userId, function(err, zScore){
          similarSum += parseFloat(zScore);
          callback();
        });
      },
      function(err){
        cb(similarSum);
      }
    );
  });
};

exports.updateRecommendationsFor = function(userId){
  userId = String(userId);
  var setsToUnion = [];
  var scoreMap = [];
  // var ratedSets = [[config.className,userId,'liked'].join(":"),[config.className,userId,'disliked'].join(":")];
  var tempSet = [config.className, userId, 'tempSet'].join(":");
  var tempDiffSet = [config.className, userId, 'tempDiffSet'].join(":");
  var similaritySet = [config.className, userId, 'similaritySet'].join(":");
  var recommendedSet = [config.className, userId, 'recommendedSet'].join(":");
  client.zrevrange(similaritySet, 0, config.nearestNeighbors-1, function(err, mostSimilarUserIds){
    client.zrange(similaritySet, 0, config.nearestNeighbors-1, function(err, leastSimilarUserIds){
      _.each(mostSimilarUserIds, function(id, key){
        setsToUnion.push([config.className,id,'liked'].join(":"));
      });
      _.each(leastSimilarUserIds, function(id, key){
        setsToUnion.push([config.className,id,'disliked'].join(":"));
      });
      if (setsToUnion.length > 0){
        async.each(setsToUnion,
          function(set, callback){
            client.sunionstore(tempSet, set, function(err){
              callback();
            });
          },
          function(err){
            client.sdiff(tempSet, [config.className,userId,'liked'].join(":"), [config.className,userId,'disliked'].join(":"), function(err, movieIds){
              async.each(movieIds,
                function(movieId, callback){
                  exports.predictFor(userId, movieId, function(score){
                    // console.log('userid', score, movieId);
                    scoreMap.push([score, movieId]);
                    callback();
                  });
                },
                function(err){
                  async.each(scoreMap,
                    function(scorePair, callback){
                      client.zadd(recommendedSet, scorePair[0], scorePair[1], function(err){
                        callback();
                      });
                    },
                    function(err){
                      client.del(tempSet, function(err){
                        client.zcard(recommendedSet, function(err, length){
                          // client.zremrangebyrank(recommendedSet, 0, length-config.numOfRecsStore-1);
                        });
                      });
                    }
                  );
                }
              );
            });
          }
        );
        // });
      }
    });
  });
};

//   async.each([1,2,3,4],
//     function(score, callback){
//       finalnum += score;
//       callback();
//     },
//     function(err){console.log('finalnum', finalnum)}
//   );
//   // var mostSimilarUserIds =
// };

exports.topSimilarUsers = function(users, person1, callback, n){
  n = n || 5;
  var name, newObj;
  var scores = [];
  _.each(users, function(value, key){
    newObj = {};
    if (person1 !== key){
      newObj[key] = pearsonCoefficient(users, person1, key);
      scores.push(newObj);
    }
  });
  scores.sort(sortArrayWithNestedObjects).reverse();
  callback(scores.splice(0,n));
};

exports.getRecommendations = function(users, person1, callback){
  var totals = {};
  var simSums = {};
  var rankings = [];
  var sim, newObj;

  for (var otherPerson in users){
    if (person1 === otherPerson){
      continue;
    } else {
      sim = pearsonCoefficient(users, person1, otherPerson);
      if (sim <= 0){
        continue;
      }
      _.each(users[otherPerson], function(rating, movieTitle){
        if (!users[person1].hasOwnProperty(movieTitle) || users[person1][movieTitle] === 0){
          totals[movieTitle] = totals[movieTitle] || 0;
          totals[movieTitle] += users[otherPerson][movieTitle] * sim;
          simSums[movieTitle] = simSums[movieTitle] || 0;
          simSums[movieTitle] += sim;
        }
      });
    }
  }
  _.each(totals, function(movieTotal, movie){
    newObj = {};
    newObj[movie] = (movieTotal / simSums[movie]);
    rankings.push(newObj);
  });
  rankings.sort(sortArrayWithNestedObjects);
  rankings.reverse();
  callback(rankings);
};

function sortArrayWithNestedObjects(a,b) {
  if (a[Object.keys(a)[0]] < b[Object.keys(b)[0]])
     return -1;
  if (a[Object.keys(a)[0]] > b[Object.keys(b)[0]])
    return 1;
  return 0;
}
<commit_msg>finally finished implementing update recommendations! it was returning odd results though so i took out the effect of dislike ratings<commit_after>var redis = require("redis"),
    client = redis.createClient();
var async = require('async');
var config = require('./config.js').config();

var jaccardCoefficient = function(userId1, userId2, callback){
  var similarity = 0,
  likedCount = 0,
  dislikedCount = 0,
  finalJaccard = 0;
  client.sinter([config.className,userId1,'liked'].join(":"),[config.className,userId2,'liked'].join(":"), function(err, results1){
    client.sinter([config.className,userId1,'disliked'].join(":"),[config.className,userId2,'disliked'].join(":"), function(err, results2){
      client.sinter([config.className,userId1,'liked'].join(":"),[config.className,userId2,'disliked'].join(":"), function(err, results3){
        client.sinter([config.className,userId1,'disliked'].join(":"),[config.className,userId2,'liked'].join(":"), function(err, results4){
          client.scard([config.className,userId1,'liked'].join(":"), function(err, likedCount){
            client.scard([config.className,userId1,'disliked'].join(":"), function(err, dislikedCount){
              similarity += (results1.length+results2.length-results3.length-results4.length);
              finalJaccardScore = similarity / (likedCount + dislikedCount);
              callback(finalJaccardScore);
            });
          });
        });
      });
    });
  });
};

exports.updateSimilarityFor = function(userId, callback){
  userId = String(userId);
  var similaritySet, userRatedMovieIds, movieLikedByUsers, movieDislikedByUsers, similarUserIds;
  similaritySet = [config.className,userId,'similaritySet'].join(":");
  client.sunion([config.className,userId,'liked'].join(":"),[config.className,userId,'disliked'].join(":"), function(err, userRatedMovieIds){
    if (userRatedMovieIds.length > 0){
      similarUserIds = _.map(userRatedMovieIds, function(otherUserRatedId, key){
        movieLikedByUsers = [config.className, otherUserRatedId, 'liked'].join(":");
        movieDislikedByUsers = [config.className, otherUserRatedId, 'disliked'].join(":");
        return [movieLikedByUsers, movieDislikedByUsers];
      });
    }
    similarUserIds = _.flatten(similarUserIds);
    client.sunion(similarUserIds, function(err, results){
      _.each(results, function(otherUserId, key){
        if (userId !== otherUserId){
          jaccardCoefficient(userId, otherUserId, function(result) {
            client.zadd(similaritySet, result, otherUserId, function(err){
              callback();
            });
          });
        }
      });
    });
  });
};

exports.predictFor = function(userId, movieId, callback){
  userId = String(userId);
  movieId = String(movieId);
  var finalSimilaritySum = 0.0;
  var prediction = 0.0;
  var similaritySet = [config.className, userId, 'similaritySet'].join(':');
  var likedBySet = [config.className, movieId, 'liked'].join(':');
  var dislikedBySet = [config.className, movieId, 'disliked'].join(':');
  exports.similaritySum(similaritySet, likedBySet, function(result1){
    exports.similaritySum(similaritySet, dislikedBySet, function(result2){
      finalSimilaritySum = result1 - result2;
      client.scard(likedBySet, function(err, likedByCount){
        client.scard(dislikedBySet, function(err, dislikedByCount){
          prediction = finalSimilaritySum / parseFloat(likedByCount + dislikedByCount);
          if (isFinite(prediction)){
            console.log('prediction', userId, prediction);
            callback(prediction);
          }
        });
      });
    });
  });
};

exports.similaritySum = function(simSet, compSet, cb){ // trying to get the score from the similarity set given the userId from the comp set
  var similarSum = 0.0;
  client.smembers(compSet, function(err, userIds){
    async.each(userIds,
      function(userId, callback){
        client.zscore(simSet, userId, function(err, zScore){
          similarSum += parseFloat(zScore);
          callback();
        });
      },
      function(err){
        cb(similarSum);
      }
    );
  });
};

exports.updateRecommendationsFor = function(userId){
  userId = String(userId);
  var setsToUnion = [];
  var scoreMap = [];
  var tempSet = [config.className, userId, 'tempSet'].join(":");
  var tempDiffSet = [config.className, userId, 'tempDiffSet'].join(":");
  var similaritySet = [config.className, userId, 'similaritySet'].join(":");
  var recommendedSet = [config.className, userId, 'recommendedSet'].join(":");
  client.zrevrange(similaritySet, 0, config.nearestNeighbors-1, function(err, mostSimilarUserIds){
    client.zrange(similaritySet, 0, config.nearestNeighbors-1, function(err, leastSimilarUserIds){
      _.each(mostSimilarUserIds, function(id, key){
        setsToUnion.push([config.className,id,'liked'].join(":"));
      });
      // _.each(leastSimilarUserIds, function(id, key){
      //   setsToUnion.push([config.className,id,'disliked'].join(":"));
      // });
      if (setsToUnion.length > 0){
        async.each(setsToUnion,
          function(set, callback){
            client.sunionstore(tempSet, set, function(err){
              callback();
            });
          },
          function(err){
            client.sdiff(tempSet, [config.className,userId,'liked'].join(":"), [config.className,userId,'disliked'].join(":"), function(err, movieIds){
              async.each(movieIds,
                function(movieId, callback){
                  exports.predictFor(userId, movieId, function(score){
                    scoreMap.push([score, movieId]);
                    callback();
                  });
                },
                function(err){
                  async.each(scoreMap,
                    function(scorePair, callback){
                      client.zadd(recommendedSet, scorePair[0], scorePair[1], function(err){
                        callback();
                      });
                    },
                    function(err){
                      client.del(tempSet, function(err){
                        client.zcard(recommendedSet, function(err, length){
                          client.zremrangebyrank(recommendedSet, 0, length-config.numOfRecsStore-1);
                        });
                      });
                    }
                  );
                }
              );
            });
          }
        );
      }
    });
  });
};

<|endoftext|>


<commit_before>'use strict';

// MODULES //

var tape = require( 'tape' );
var Float64Array = require( '@stdlib/types/array/float64' ); // eslint-disable-line no-redeclare
var hasOwnProp = require( '@stdlib/assert/has-own-property' );
var hasProp = require( '@stdlib/assert/has-property' );
var isFunction = require( '@stdlib/assert/is-function' );
var ctor = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof ctor, 'function', 'main export is a function' );
	t.end();
});

tape( 'an ndarray constructor returns an instance which has a `get` method which throws an error if not provided an integer value (>4d)', function test( t ) {
	var strides;
	var values;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var arr;
	var f;
	var i;
	var j;

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 1, 1, 2, 2 ];
	order = 'row-major';
	strides = [ 2, 1, 1, 1, 1 ];
	offset = 0;

	f = ctor( dtype, shape.length );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	values = [
		'5',
		3.14,
		NaN,
		true,
		false,
		null,
		void 0,
		[],
		{},
		function noop() {}
	];
	for ( i = 0; i < values.length; i++ ) {
		for ( j = 0; j < shape.length; j++ ) {
			t.throws( badValue( values[ i ], j ), TypeError, 'throws an error when provided ' + values[ i ] );
		}
	}
	t.end();

	function badValue( value, dim ) {
		return function badValue() {
			var args = new Array( shape.length );
			var i;

			for ( i = 0; i < args.length; i++ ) {
				if ( i === dim ) {
					args[ i ] = value;
				} else {
					args[ i ] = 0;
				}
			}
			arr.get.apply( arr, args );
		};
	}
});

tape( 'an ndarray constructor returns an instance which has a `get` method for retrieving an array element using subscripts (>4d; row-major)', function test( t ) {
	var strides;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var arr;
	var f;

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 2, 2, 1, 1 ];
	order = 'row-major';
	strides = [ 4, 2, 1, 1, 1 ];
	offset = 0;

	f = ctor( dtype, shape.length );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	t.strictEqual( arr.get( 0, 0, 0, 0, 0 ), 1.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 1, 0, 0 ), 2.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 1, 0, 0, 0 ), 3.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 1, 1, 0, 0 ), 4.0, 'returns expected value' );

	t.end();
});

tape( 'an ndarray constructor returns an instance which has a `get` method for retrieving an array element using subscripts (>4d; column-major)', function test( t ) {
	var strides;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var arr;
	var f;

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 1, 2, 2, 1 ];
	order = 'column-major';
	strides = [ 1, 1, 1, 2, 4 ];
	offset = 0;

	f = ctor( dtype, shape.length );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	t.strictEqual( arr.get( 0, 0, 0, 0, 0 ), 1.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 0, 1, 0 ), 3.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 1, 0, 0 ), 2.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 1, 1, 0 ), 4.0, 'returns expected value' );

	t.end();
});
<commit_msg>Add tests for returning >4d array elements in wrap mode<commit_after>'use strict';

// MODULES //

var tape = require( 'tape' );
var Float64Array = require( '@stdlib/types/array/float64' ); // eslint-disable-line no-redeclare
var hasOwnProp = require( '@stdlib/assert/has-own-property' );
var hasProp = require( '@stdlib/assert/has-property' );
var isFunction = require( '@stdlib/assert/is-function' );
var ctor = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof ctor, 'function', 'main export is a function' );
	t.end();
});

tape( 'an ndarray constructor returns an instance which has a `get` method which throws an error if not provided an integer value (>4d)', function test( t ) {
	var strides;
	var values;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var arr;
	var f;
	var i;
	var j;

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 1, 1, 2, 2 ];
	order = 'row-major';
	strides = [ 2, 1, 1, 1, 1 ];
	offset = 0;

	f = ctor( dtype, shape.length );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	values = [
		'5',
		3.14,
		NaN,
		true,
		false,
		null,
		void 0,
		[],
		{},
		function noop() {}
	];
	for ( i = 0; i < values.length; i++ ) {
		for ( j = 0; j < shape.length; j++ ) {
			t.throws( badValue( values[ i ], j ), TypeError, 'throws an error when provided ' + values[ i ] );
		}
	}
	t.end();

	function badValue( value, dim ) {
		return function badValue() {
			var args = new Array( shape.length );
			var i;

			for ( i = 0; i < args.length; i++ ) {
				if ( i === dim ) {
					args[ i ] = value;
				} else {
					args[ i ] = 0;
				}
			}
			arr.get.apply( arr, args );
		};
	}
});

tape( 'an ndarray constructor returns an instance which has a `get` method for retrieving an array element using subscripts (>4d; row-major)', function test( t ) {
	var strides;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var arr;
	var f;

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 2, 2, 1, 1 ];
	order = 'row-major';
	strides = [ 4, 2, 1, 1, 1 ];
	offset = 0;

	f = ctor( dtype, shape.length );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	t.strictEqual( arr.get( 0, 0, 0, 0, 0 ), 1.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 1, 0, 0 ), 2.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 1, 0, 0, 0 ), 3.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 1, 1, 0, 0 ), 4.0, 'returns expected value' );

	t.end();
});

tape( 'an ndarray constructor returns an instance which has a `get` method for retrieving an array element using subscripts (>4d; column-major)', function test( t ) {
	var strides;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var arr;
	var f;

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 1, 2, 2, 1 ];
	order = 'column-major';
	strides = [ 1, 1, 1, 2, 4 ];
	offset = 0;

	f = ctor( dtype, shape.length );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	t.strictEqual( arr.get( 0, 0, 0, 0, 0 ), 1.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 0, 1, 0 ), 3.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 1, 0, 0 ), 2.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 1, 1, 0 ), 4.0, 'returns expected value' );

	t.end();
});

tape( 'an ndarray constructor returns an instance which has a `get` method for retrieving an array element using subscripts (>4d; row-major; mode=wrap)', function test( t ) {
	var strides;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var opts;
	var arr;
	var f;

	opts = {
		'mode': 'wrap'
	};

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 2, 2, 1, 1 ];
	order = 'row-major';
	strides = [ 4, 2, 1, 1, 1 ];
	offset = 0;

	f = ctor( dtype, shape.length, opts );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	t.strictEqual( arr.get( 0, 10, 10, 0, 0 ), 1.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 5, 0, 0 ), 2.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 5, 6, 0, 0 ), 3.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, -1, -1, 0, 0 ), 4.0, 'returns expected value' );

	t.end();
});

tape( 'an ndarray constructor returns an instance which has a `get` method for retrieving an array element using subscripts (>4d; column-major; mode=wrap)', function test( t ) {
	var strides;
	var buffer;
	var offset;
	var dtype;
	var order;
	var shape;
	var opts;
	var arr;
	var f;

	opts = {
		'mode': 'wrap'
	};

	dtype = 'float64';
	buffer = new Float64Array( [ 1.0, 2.0, 3.0, 4.0 ] );
	shape = [ 1, 2, 2, 1, 1 ];
	order = 'column-major';
	strides = [ 1, 1, 1, 2, 4 ];
	offset = 0;

	f = ctor( dtype, shape.length, opts );
	arr = f( buffer, shape, strides, offset, order );

	t.strictEqual( hasOwnProp( arr, 'get' ), false, 'does not have own property' );
	t.strictEqual( hasProp( arr, 'get' ), true, 'has property' );
	t.strictEqual( isFunction( arr.get ), true, 'has method' );

	t.strictEqual( arr.get( 0, 10, 10, 0, 0 ), 1.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 0, 5, 0, 0 ), 3.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, 5, 6, 0, 0 ), 2.0, 'returns expected value' );
	t.strictEqual( arr.get( 0, -1, -1, 0, 0 ), 4.0, 'returns expected value' );

	t.end();
});
<|endoftext|>


<commit_before>
var App = require('view').extend(function(){

	this.nested = {
		Background: require('shaders/rectshader').extend(function(){
			this.pixel = function(){
				return mix('red','green',mesh.y)
			}
		})
	}

	this.ondraw = function(){
		this.drawBackground({
			color:'orange',
			x:-.5,
			y:-.5,
			w:1,
			h:1
		})
	}
})
App().runApp()
<commit_msg>working rect<commit_after>
var App = require('view').extend(function(){

	this.nested = {
		Background: require('shaders/rectshader').extend(function(){
			this.pixel = function(){
				return props.color
			}
		})
	}

	this.ondraw = function(){
		for(var i = 0 ;i < 10000; i++){
			this.drawBackground({
				color:[0,.5+.5*Math.sin(i*0.1),.5+.5*Math.sin(i*0.1),1],
				x:-.5 + Math.sin(i*.01)*.5+.5,
				y:-.5 + Math.cos(i*.03)*.5+.5,
				w:.05,
				h:.05
			})
		}
	}
})
App().runApp()
<|endoftext|>


<commit_before>var root = require('root-path')
require(root('test/setup'))
var factories = require(root('test/setup/factories'))
var InvitationController = require(root('api/controllers/InvitationController'))
import { mockify, unspyify } from '../../setup/helpers'

describe('InvitationController', () => {
  var user, community, invitation, inviter, req, res

  before(() => {
    req = factories.mock.request()
    res = factories.mock.response()
  })

  describe('.use', () => {
    before(() => {
      user = factories.user()
      inviter = factories.user()
      community = factories.community()
      return Promise.join(inviter.save(), user.save(), community.save())
      .then(() => {
        req.login(user.id)

        return Invitation.create({
          communityId: community.id,
          userId: inviter.id,
          email: 'foo@bar.com'
        }).tap(i => {
          invitation = i
          req.params.token = invitation.get('token')
        })
      })
    })

    it('adds the user to the community', () => {
      return InvitationController.use(req, res)
      .then(() => {
        expect(res.ok).to.have.been.called()
        return user.load(['communities'])
      })
      .then(() => {
        expect(user.relations.communities.first().id).to.equal(community.id)
      })
    })
  })

  describe('.create', () => {
    var community, u1, u2

    before(() => {
      community = factories.community()
      u1 = factories.user()
      u2 = factories.user()
      return Promise.join(
        community.save(),
        u1.save(),
        u2.save())
    })

    beforeEach(() => {
      req.session.userId = user.id
      mockify(Email, 'sendInvitation', () => new Promise((res, rej) => res()))
    })

    afterEach(() => unspyify(Email, 'sendInvitation'))

    it('rejects invalid email', () => {
      _.extend(req.params, {communityId: community.id, emails: 'wow, lol'})

      return InvitationController.create(req, res)
      .then(() => {
        expect(res.body).to.deep.equal({
          results: [
            {email: 'wow', error: 'not a valid email address'},
            {email: 'lol', error: 'not a valid email address'}
          ]
        })
      })
    })

    it('sends invitations to emails and users', () => {
      _.extend(req.params, {
        communityId: community.id,
        emails: 'foo@bar.com, bar@baz.com',
        users: [u1.id, u2.id]
      })

      return InvitationController.create(req, res)
      .then(() => {
        expect(Email.sendInvitation).to.have.been.called.exactly(4)

        expect(res.body).to.deep.equal({
          results: [
            {email: 'foo@bar.com', error: null},
            {email: 'bar@baz.com', error: null},
            {email: u1.get('email'), error: null},
            {email: u2.get('email'), error: null}
          ]
        })
      })
    })

    it('returns error message if mail sending fails', () => {
      mockify(Email, 'sendInvitation', () => new Promise((res, rej) => rej({message: 'failed'})))
      _.extend(req.params, {communityId: community.id, emails: 'foo@bar.com, bar@baz.com', users: []})

      return InvitationController.create(req, res)
      .then(() => {
        expect(Email.sendInvitation).to.have.been.called.exactly(2)

        expect(res.body).to.deep.equal({
          results: [
            {email: 'foo@bar.com', error: 'failed'},
            {email: 'bar@baz.com', error: 'failed'}
          ]
        })
      })
    })
  })
})
<commit_msg>make tests deterministic<commit_after>var root = require('root-path')
require(root('test/setup'))
var factories = require(root('test/setup/factories'))
var InvitationController = require(root('api/controllers/InvitationController'))
import { mockify, unspyify } from '../../setup/helpers'
import { sortBy } from 'lodash/fp'

describe('InvitationController', () => {
  var user, community, invitation, inviter, req, res

  before(() => {
    req = factories.mock.request()
    res = factories.mock.response()
  })

  describe('.use', () => {
    before(() => {
      user = factories.user()
      inviter = factories.user()
      community = factories.community()
      return Promise.join(inviter.save(), user.save(), community.save())
      .then(() => {
        req.login(user.id)

        return Invitation.create({
          communityId: community.id,
          userId: inviter.id,
          email: 'foo@bar.com'
        }).tap(i => {
          invitation = i
          req.params.token = invitation.get('token')
        })
      })
    })

    it('adds the user to the community', () => {
      return InvitationController.use(req, res)
      .then(() => {
        expect(res.ok).to.have.been.called()
        return user.load(['communities'])
      })
      .then(() => {
        expect(user.relations.communities.first().id).to.equal(community.id)
      })
    })
  })

  describe('.create', () => {
    var community, u1, u2

    before(() => {
      community = factories.community()
      u1 = factories.user()
      u2 = factories.user()
      return Promise.join(
        community.save(),
        u1.save(),
        u2.save())
    })

    beforeEach(() => {
      req.session.userId = user.id
      mockify(Email, 'sendInvitation', () => new Promise((res, rej) => res()))
    })

    afterEach(() => unspyify(Email, 'sendInvitation'))

    it('rejects invalid email', () => {
      _.extend(req.params, {communityId: community.id, emails: 'wow, lol'})

      return InvitationController.create(req, res)
      .then(() => {
        expect(res.body).to.deep.equal({
          results: [
            {email: 'wow', error: 'not a valid email address'},
            {email: 'lol', error: 'not a valid email address'}
          ]
        })
      })
    })

    it('sends invitations to emails and users', () => {
      _.extend(req.params, {
        communityId: community.id,
        emails: 'foo@bar.com, bar@baz.com',
        users: [u1.id, u2.id]
      })

      return InvitationController.create(req, res)
      .then(() => {
        expect(Email.sendInvitation).to.have.been.called.exactly(4)

        expect(sortBy('email', res.body.results)).to.deep.equal(
          sortBy('email', [
            {email: 'foo@bar.com', error: null},
            {email: 'bar@baz.com', error: null},
            {email: u1.get('email'), error: null},
            {email: u2.get('email'), error: null}
          ]))
      })
    })

    it('returns error message if mail sending fails', () => {
      mockify(Email, 'sendInvitation', () => new Promise((res, rej) => rej({message: 'failed'})))
      _.extend(req.params, {communityId: community.id, emails: 'foo@bar.com, bar@baz.com', users: []})

      return InvitationController.create(req, res)
      .then(() => {
        expect(Email.sendInvitation).to.have.been.called.exactly(2)

        expect(res.body).to.deep.equal({
          results: [
            {email: 'foo@bar.com', error: 'failed'},
            {email: 'bar@baz.com', error: 'failed'}
          ]
        })
      })
    })
  })
})
<|endoftext|>


<commit_before>module.exports = [
  'installation',
  'getting-started',
  'basics',
  {
    type: 'category',
    label: 'Main Concepts',
    items: [
      'hosts',
      'tasks',
      'config',
    ],
  },
  'yaml',
  'cli',
  'api',
]
<commit_msg>Add other docs section<commit_after>module.exports = [
  'installation',
  'getting-started',
  'basics',
  {
    type: 'category',
    label: 'Main Concepts',
    items: [
      'hosts',
      'tasks',
      'config',
    ],
  },
  'yaml',
  'cli',
  'api',
  {
    type: 'category',
    label: 'Other',
    items: [
      'UPGRADE',
    ],
  },
]
<|endoftext|>


<commit_before>angular.module('Angular360', []).directive('vrCube', ['$window', function($window) {
  return {
    restrict: 'E',
    transclude: true,
    template: '
'
             +  '
'
             +    '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +    '
'
             +  '
'
             +'
'
             +'

',
    scope: {
      x:          '=?',
      y:          '=?',
      size:       '=?',
      fullscreen: '=?',
      front:      '@',
      left:       '@',
      right:      '@',
      back:       '@',
      top:        '@',
      bottom:     '@',
      markers:    '=',
      debug:      '=?'
    },
    controller: ['$scope', '$window', function($scope, $window) {
      // default values
      $scope.x = $scope.x || 0;
      $scope.y = $scope.y || 0;

      // set fullscreen enable if not set
      if (!angular.isDefined($scope.fullscreen)) {
        $scope.fullscreen = true;
      }

      if ($scope.fullscreen) {
        setFullscreen();

        // handle window size change
        angular.element($window).on('resize', function() {
          setFullscreen();
          $scope.$apply();
        });
      } else {
        $scope.size = $scope.size || Math.min($window.innerWidth, $window.innerHeight);
      }

      function setFullscreen() {
        // set size to cover whole window
        $scope.size = Math.max($window.innerWidth, $window.innerHeight);

        // centering
        $scope.marginLeft = ($window.innerWidth  - $scope.size) * 0.5;
        $scope.marginTop  = ($window.innerHeight - $scope.size) * 0.5;
      }
    }],
    link: function(scope, element) {
      // Last event's position.
      var lastPos;
      // Whether a swipe is active.
      var active = false;

      var scrollSensitivity;

      setScrollSensitivity();
      angular.element($window).on('resize', setScrollSensitivity);

      element.on('touchstart mousedown', function(event) {
        lastPos = getCoordinates(event);
        active = true;
      }).on('touchcancel', function(event) {
        active = false;
      }).on('touchmove mousemove', function(event) {
        if (!active) return;

        var coords = getCoordinates(event);
        var dx = (coords.x - lastPos.x) * scrollSensitivity;
        var dy = (coords.y - lastPos.y) * scrollSensitivity;

        // rotate view
        scope.y -= dx;
        scope.x += dy;

        // prevent over rotation by X
        if (scope.x > 90) {
          scope.x = 90;
        } else if (scope.x < -90) {
          scope.x = -90;
        }

        lastPos = coords;
        event.preventDefault();
        scope.$apply();
      }).on('touchend mouseup', function(event) {
        active = false;
      });

      // iOS hack (http://blog.choilabo.com/20120316/18)
      element.parent().on('touchstart touchmove touchend', angular.noop);

      // put transclude contents (need jQuery)
      element.find('.vr-cube-face-front').append(element.find('vr-front'));
      element.find('.vr-cube-face-left').append(element.find('vr-left'));
      element.find('.vr-cube-face-right').append(element.find('vr-right'));
      element.find('.vr-cube-face-back').append(element.find('vr-back'));
      element.find('.vr-cube-face-top').append(element.find('vr-top'));
      element.find('.vr-cube-face-bottom').append(element.find('vr-bottom'));

      function setScrollSensitivity() {
        // scrolling device's left edge to right edge equals 360 rotation
        scrollSensitivity = 360 / $window.innerWidth;
      }
    }
  }

  /**
   * from angular-touch.js
   */
  function getCoordinates(event) {
    var touches = event.touches && event.touches.length ? event.touches : [event];
    var e = (event.changedTouches && event.changedTouches[0]) ||
        (event.originalEvent && event.originalEvent.changedTouches &&
            event.originalEvent.changedTouches[0]) ||
        touches[0].originalEvent || touches[0];

    return {
      x: e.clientX,
      y: e.clientY
    };
  }
}]);<commit_msg>add gyro support<commit_after>angular.module('Angular360', []).directive('vrCube', ['$window', function($window) {
  return {
    restrict: 'E',
    transclude: true,
    template: '
'
             +  '
'
             +    '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +      '
'
             +        ''
             +      '
'
             +    '
'
             +  '
'
             +'
'
             +'

',
    scope: {
      x:          '=?',
      y:          '=?',
      size:       '=?',
      fullscreen: '=?',
      front:      '@',
      left:       '@',
      right:      '@',
      back:       '@',
      top:        '@',
      bottom:     '@',
      markers:    '=',
      gyro:       '=?',
      debug:      '=?'
    },
    controller: ['$scope', '$window', function($scope, $window) {
      // default values
      $scope.x = $scope.x || 0;
      $scope.y = $scope.y || 0;
      $scope.offsetX = 0;
      $scope.offsetY = 0;

      // set fullscreen enable if not set
      if (!angular.isDefined($scope.fullscreen)) {
        $scope.fullscreen = true;
      }

      if ($scope.fullscreen) {
        setFullscreen();

        // handle window size change
        angular.element($window).on('resize', function() {
          setFullscreen();
          $scope.$apply();
        });
      } else {
        $scope.size = $scope.size || Math.min($window.innerWidth, $window.innerHeight);
      }

      function setFullscreen() {
        // set size to cover whole window
        $scope.size = Math.max($window.innerWidth, $window.innerHeight);

        // centering
        $scope.marginLeft = ($window.innerWidth  - $scope.size) * 0.5;
        $scope.marginTop  = ($window.innerHeight - $scope.size) * 0.5;
      }
    }],
    link: function(scope, element) {
      // Last event's position.
      var lastPos;
      // Whether a swipe is active.
      var active = false;

      var scrollSensitivity;

      setScrollSensitivity();
      angular.element($window).on('resize', setScrollSensitivity);

      // touch support
      element.on('touchstart mousedown', function(event) {
        lastPos = getCoordinates(event);
        active = true;
      }).on('touchcancel', function(event) {
        active = false;
      }).on('touchmove mousemove', function(event) {
        if (!active) return;

        var coords = getCoordinates(event);
        var dx = (coords.x - lastPos.x) * scrollSensitivity;
        var dy = (coords.y - lastPos.y) * scrollSensitivity;

        // rotate view
        scope.y -= dx;
        scope.x += dy;
        scope.offsetY -= dx;

        // prevent over rotation by X
        if (scope.x > 90) {
          scope.x = 90;
        } else if (scope.x < -90) {
          scope.x = -90;
        } else {
          scope.offsetX += dy;
        }

        lastPos = coords;
        event.preventDefault();
        scope.$apply();
      }).on('touchend mouseup', function(event) {
        active = false;
      });

      // iOS hack (http://blog.choilabo.com/20120316/18)
      element.parent().on('touchstart touchmove touchend', angular.noop);

      // gyro
      if ($window.DeviceOrientationEvent) {
        $window.addEventListener('deviceorientation', new DeviceOrientationHandler(scope));
      }

      function setScrollSensitivity() {
        // scrolling device's left edge to right edge equals 360 rotation
        scrollSensitivity = (360 / $window.innerWidth + 180 / $window.innerHeight) * 0.5;
      }
    }
  }

  /**
   * from angular-touch.js
   */
  function getCoordinates(event) {
    var touches = event.touches && event.touches.length ? event.touches : [event];
    var e = (event.changedTouches && event.changedTouches[0]) ||
        (event.originalEvent && event.originalEvent.changedTouches &&
            event.originalEvent.changedTouches[0]) ||
        touches[0].originalEvent || touches[0];

    return {
      x: e.clientX,
      y: e.clientY
    };
  }

  function DeviceOrientationHandler(scope) {
    var degRad = Math.PI / 180,
      hLookAt = 0,
      vLookAt = 0,
      friction = 0.5;

    return function(event) {
      if (!scope.gyro) return;

      var deviceOrientation = top.orientation,
        orientation = rotateEuler({
          yaw: event.alpha * degRad,
          pitch: event.beta * degRad,
          roll: event.gamma * degRad
        }),
        yaw = wrapAngle(orientation.yaw / degRad),
        pitch = orientation.pitch / degRad,
        altYaw = yaw,
        factor,
        hLookAtNow = hLookAt,
        vLookAtNow = vLookAt;

      // Fix gimbal lock
      if (Math.abs(pitch) > 70) {
        altYaw = event.alpha;

        switch (deviceOrientation) {
          case 0:
            if (pitch > 0)
              altYaw += 180;
            break;
          case 90:
            altYaw += 90;
            break;
          case -90:
            altYaw += -90;
            break;
          case 180:
            if (pitch < 0)
              altYaw += 180;
            break;
        }

        altYaw = wrapAngle(altYaw);
        if (Math.abs(altYaw - yaw) > 180)
          altYaw += (altYaw < yaw) ? 360 : -360;

        factor = Math.min(1, (Math.abs(pitch) - 70) / 10);
        yaw = yaw * (1 - factor) + altYaw * factor;
      }

      hLookAt = wrapAngle(-yaw - 180);
      vLookAt = Math.max(Math.min((pitch), 90), -90);

      // Dampen lookat
      if (Math.abs(hLookAt - hLookAtNow) > 180)
        hLookAtNow += (hLookAt > hLookAtNow) ? 360 : -360;

      hLookAt = (1 - friction) * hLookAt + friction * hLookAtNow;
      vLookAt = (1 - friction) * vLookAt + friction * vLookAtNow;

      scope.y = wrapAngle(hLookAt) + scope.offsetY;
      scope.x = -vLookAt + scope.offsetX;
      scope.$apply();

      function rotateEuler(euler) {
        // This function is based on http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm
        // and http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm

        var heading, bank, attitude,
          ch = Math.cos(euler.yaw),
          sh = Math.sin(euler.yaw),
          ca = Math.cos(euler.pitch),
          sa = Math.sin(euler.pitch),
          cb = Math.cos(euler.roll),
          sb = Math.sin(euler.roll),

          matrix = [
            sh * sb - ch * sa * cb, -ch * ca, ch * sa * sb + sh * cb,
            ca * cb, -sa, -ca * sb,
            sh * sa * cb + ch * sb, sh * ca, -sh * sa * sb + ch * cb
          ]; // Note: Includes 90 degree rotation around z axis

        /* [m00 m01 m02] 0 1 2
         * [m10 m11 m12] 3 4 5
         * [m20 m21 m22] 6 7 8 */

        if (matrix[3] > 0.9999) {
          // Deal with singularity at north pole
          heading = Math.atan2(matrix[2], matrix[8]);
          attitude = Math.PI / 2;
          bank = 0;
        } else if (matrix[3] < -0.9999) {
          // Deal with singularity at south pole
          heading = Math.atan2(matrix[2], matrix[8]);
          attitude = -Math.PI / 2;
          bank = 0;
        } else {
          heading = Math.atan2(-matrix[6], matrix[0]);
          bank = Math.atan2(-matrix[5], matrix[4]);
          attitude = Math.asin(matrix[3]);
        }

        return {
          yaw: heading,
          pitch: attitude,
          roll: bank
        };
      }

      function wrapAngle(value) {
        value = value % 360;
        return (value <= 180) ? value : value - 360;
      } // wrap a value between -180 and 180
    }
  }
}]);<|endoftext|>


<commit_before>var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var net = require('net');
var semaphore = require('semaphore')(1);
var conf = require('mbc-common').config.Mosto.Melted;
var melted_bin_path = conf.root + '/melted/BUILD/bin/melted';
var melted_lib_path = conf.root + '/melted/BUILD/lib';
var logger = require('../logger').addLogger('MELTED'),

_meltedbin = function(callback,errorCallback) {
    logger.debug("Executing _meltedbin()");

    var pbin = melted_bin_path;

    exec('which melted', function(error, stdout, stderr) {
        pbin = "melted";
        conf.bin = pbin;
        logger.debug('Melted binary: ' + stdout);
        return callback(pbin);
    });

};

/**
 * _do
 *
 * Private function to connect to melted.
 *
 * @callback: Callback function to do to melted.
 */
_do = function(callback) {
    //      var pgrep = spawn('pgrep', ['-x', "melted"]);
//    var pgrep = spawn('pgrep', ["melted"]);
//    var pid;
//
//    pgrep.stdout.on('data', function (data) {
//        pid = data;
//        console.log("Melted.js: [INFO] _do pid : " + pid );
//    });
//
//    pgrep.on('exit', function (code) {
//        return callback(parseInt(pid));
//    });
    exec('pgrep melted', function(error, stdout, stderr) {
        var pid = stdout;
        logger.debug("Melted.js: [INFO] _do pid : " + pid );
        return callback(parseInt(pid));
    });
};

/**
 * update_config
 *
 * Setup following melted attributes:
 *
 *   - bin: path to binary file.
 *   - root: path to videos files.
 *   - host: host where melted is running.
 *   - port: port where melted is running.
 *   - output: output device.
 *
 * @conf: Map with melted basic configuration.
 */
exports.update_config = function(new_config) {
    conf.bin   = new_config.bin || conf.bin;
    conf.root  = new_config.root || conf.root;
    conf.host  = new_config.host || conf.host;
    conf.port  = new_config.port || conf.port;
    conf.output= new_config.output || conf.output;
};

/**
 * get_config
 *
 * Return config object.
 */
exports.get_config = function() {
    return conf;
};

/**
 * is_running
 *
 * @callback: callback function when known if running or not.
 */
exports.is_running = function(callback) {
    _do(function(pid) {
        return callback(!isNaN(pid));
    })
}

/**
 * stop
 *
 * Stop melted.
 *
 * @callback : callback function when process is stopped.
 */
exports.stop = function(callback) {
    _do(function(pid) {
        if (pid) {
            //exports.connect(function(conn){ exports.push(conn, ['SHUTDOWN'], undefined, undefined); });
            var kill = spawn('kill',['-9',pid]);
            //kill.on('close', function(state) { return callback(pid) });
            kill.on('exit', function(code) {
                if (code) logger.debug("Returned with code:"+code);
                return callback(pid)
            });
        } else
            callback(pid);
    })
};

/**
 * start
 *
 * Start melted deamon.
 *
 * @callback : callback function when process is ready.
 */
exports.start = function(callback) {
    _do(function(pid) {
        if (pid) {
            callback(pid);
        } else {
            _meltedbin( function(lbin) {
                logger.debug("Melted.start > melted_bin is at: "+conf.bin);

                if (process.env.LD_LIBRARY_PATH!==undefined) process.env.LD_LIBRARY_PATH = process.env.LD_LIBRARY_PATH+":"+melted_lib_path;
                else process.env.LD_LIBRARY_PATH = melted_lib_path;

                var melted_proc = spawn(conf.bin, [], {detached: true, stdio: [ 'ignore', 'ignore', 'ignore' ]});
                var pid = melted_proc.pid;
                melted_proc.on('exit', function(code) {
                    setTimeout(function() { callback(pid); }, 1000 );
                } );
            }, function(error) {
                callback(0);
            } );
        }
    })
};

/**
 * connect
 *
 * Connect to melted daemon.
 *
 * @callback : callback function when process is ready.
 */
exports.connect = function(callback) {
    var conn = net.createConnection(conf.port, conf.host);
    conn.setEncoding('ascii');
    callback(conn);
};

/**
 * push
 *
 * Push commands to melted daemon.
 *
 * @commands : list of commands to send.
 * @callback : callback function when process is ready.
 */
exports.push = function(conn, commands, command_callback, close_callback) {
    var s = 0;
    conn.on('connect', function() {
        conn.on('data', function(data) {
            if (command_callback) command_callback(data);
            if (s < commands.length && data.indexOf("\n") > -1) {
                conn.write(commands[s]+"\n");
                s = s + 1;
            };
        });
    });
    if (close_callback) conn.on('close', function(had_err) { close_callback(had_err); });
};

/**
 * setup
 *
 * Setup melted deamon.
 *
 * @callback : callback function when process is ready.
 */
exports.setup = function(root, output, callback) {
    root = root || conf.root;
    output = output || conf.output;
    _do(function(pid) {
        if (pid) {
            logger.debug("Melted.setup > setting up root:" + root + " ouput:" + output);
            var commands = [ 'NLS', 'SET root='+root, 'UADD '+output, 'BYE' ];
            exports.connect(function(conn){ exports.push(conn, commands, undefined, callback)});
        } else {
            callback(new Error("Can't connect to server. Server is not running!"));
        };
    })
};

/**
 * take
 *
 * Take melted and not leave execution to other melted taked.
 *
 * @callback: callback function to process while take melted.
 *
 */
exports.take = semaphore.take;

/**
 * leave
 *
 * Leave execution to other melted taked.
 *
 */
exports.leave = semaphore.leave;
<commit_msg>Removed commented code<commit_after>var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var net = require('net');
var semaphore = require('semaphore')(1);
var conf = require('mbc-common').config.Mosto.Melted;
var melted_bin_path = conf.root + '/melted/BUILD/bin/melted';
var melted_lib_path = conf.root + '/melted/BUILD/lib';
var logger = require('../logger').addLogger('MELTED'),

_meltedbin = function(callback,errorCallback) {
    logger.debug("Executing _meltedbin()");

    var pbin = melted_bin_path;

    exec('which melted', function(error, stdout, stderr) {
        pbin = "melted";
        conf.bin = pbin;
        logger.debug('Melted binary: ' + stdout);
        return callback(pbin);
    });

};

/**
 * _do
 *
 * Private function to connect to melted.
 *
 * @callback: Callback function to do to melted.
 */
_do = function(callback) {
    exec('pgrep melted', function(error, stdout, stderr) {
        var pid = stdout;
        logger.debug("Melted.js: [INFO] _do pid : " + pid );
        return callback(parseInt(pid));
    });
};

/**
 * update_config
 *
 * Setup following melted attributes:
 *
 *   - bin: path to binary file.
 *   - root: path to videos files.
 *   - host: host where melted is running.
 *   - port: port where melted is running.
 *   - output: output device.
 *
 * @conf: Map with melted basic configuration.
 */
exports.update_config = function(new_config) {
    conf.bin   = new_config.bin || conf.bin;
    conf.root  = new_config.root || conf.root;
    conf.host  = new_config.host || conf.host;
    conf.port  = new_config.port || conf.port;
    conf.output= new_config.output || conf.output;
};

/**
 * get_config
 *
 * Return config object.
 */
exports.get_config = function() {
    return conf;
};

/**
 * is_running
 *
 * @callback: callback function when known if running or not.
 */
exports.is_running = function(callback) {
    _do(function(pid) {
        return callback(!isNaN(pid));
    })
}

/**
 * stop
 *
 * Stop melted.
 *
 * @callback : callback function when process is stopped.
 */
exports.stop = function(callback) {
    _do(function(pid) {
        if (pid) {
            //exports.connect(function(conn){ exports.push(conn, ['SHUTDOWN'], undefined, undefined); });
            var kill = spawn('kill',['-9',pid]);
            //kill.on('close', function(state) { return callback(pid) });
            kill.on('exit', function(code) {
                if (code) logger.debug("Returned with code:"+code);
                return callback(pid)
            });
        } else
            callback(pid);
    })
};

/**
 * start
 *
 * Start melted deamon.
 *
 * @callback : callback function when process is ready.
 */
exports.start = function(callback) {
    _do(function(pid) {
        if (pid) {
            callback(pid);
        } else {
            _meltedbin( function(lbin) {
                logger.debug("Melted.start > melted_bin is at: "+conf.bin);

                if (process.env.LD_LIBRARY_PATH!==undefined) process.env.LD_LIBRARY_PATH = process.env.LD_LIBRARY_PATH+":"+melted_lib_path;
                else process.env.LD_LIBRARY_PATH = melted_lib_path;

                var melted_proc = spawn(conf.bin, [], {detached: true, stdio: [ 'ignore', 'ignore', 'ignore' ]});
                var pid = melted_proc.pid;
                melted_proc.on('exit', function(code) {
                    setTimeout(function() { callback(pid); }, 1000 );
                } );
            }, function(error) {
                callback(0);
            } );
        }
    })
};

/**
 * connect
 *
 * Connect to melted daemon.
 *
 * @callback : callback function when process is ready.
 */
exports.connect = function(callback) {
    var conn = net.createConnection(conf.port, conf.host);
    conn.setEncoding('ascii');
    callback(conn);
};

/**
 * push
 *
 * Push commands to melted daemon.
 *
 * @commands : list of commands to send.
 * @callback : callback function when process is ready.
 */
exports.push = function(conn, commands, command_callback, close_callback) {
    var s = 0;
    conn.on('connect', function() {
        conn.on('data', function(data) {
            if (command_callback) command_callback(data);
            if (s < commands.length && data.indexOf("\n") > -1) {
                conn.write(commands[s]+"\n");
                s = s + 1;
            };
        });
    });
    if (close_callback) conn.on('close', function(had_err) { close_callback(had_err); });
};

/**
 * setup
 *
 * Setup melted deamon.
 *
 * @callback : callback function when process is ready.
 */
exports.setup = function(root, output, callback) {
    root = root || conf.root;
    output = output || conf.output;
    _do(function(pid) {
        if (pid) {
            logger.debug("Melted.setup > setting up root:" + root + " ouput:" + output);
            var commands = [ 'NLS', 'SET root='+root, 'UADD '+output, 'BYE' ];
            exports.connect(function(conn){ exports.push(conn, commands, undefined, callback)});
        } else {
            callback(new Error("Can't connect to server. Server is not running!"));
        };
    })
};

/**
 * take
 *
 * Take melted and not leave execution to other melted taked.
 *
 * @callback: callback function to process while take melted.
 *
 */
exports.take = semaphore.take;

/**
 * leave
 *
 * Leave execution to other melted taked.
 *
 */
exports.leave = semaphore.leave;
<|endoftext|>


<commit_before>export { Base };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as TwoTensors from "../../util/TwoTensors.js";
import * as ReturnOrClone_Activation from "../ReturnOrClone_Activation.js";
import * as BoundsArraySet from "../BoundsArraySet.js";
import { FiltersArray_BiasesArray } from "./Depthwise_FiltersArray_BiasesArray.js";

/**
 * Handle depthwise convolution, bias and activation.
 *
 * @member {boolean} bExisted
 *   If true, this depthwise operation exists. The same as this.bDepthwise.
 *
 * @member {boolean} bDepthwise
 *   If true, this depthwise operation exists. The same as this.bExisted.
 *
 * @member {boolean} bDepthwiseAvg
 *   If true, this depthwise operation exists. And it is depthwise average pooling.
 *
 * @member {boolean} bDepthwiseMax
 *   If true, this depthwise operation exists. And it is depthwise maximum pooling.
 *
 * @member {boolean} bDepthwiseConv
 *   If true, this depthwise operation exist. And it is depthwise convolution.
 *
 * @member {boolean} bInitOk
 *   If true, the init() is successful.
 *
 * @member {function} pfnOperation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensor may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Avg_and_destroy(), Avg_and_keep(), Max_and_destroy(), Max_and_keep(),
 * Conv_and_destroy(), Conv_and_keep() according to the parameters.
 *
 * @member {function} pfnOperationBiasActivation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensors may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Operation_and_destroy_or_keep(), OperationBias_and_destroy_or_keep(),
 * OperationActivation_and_destroy_or_keep(), OperationBiasActivation_and_destroy_or_keep() according to the parameters.
 *
 * @see FiltersArray_BiasesArray
 */
class Base extends FiltersArray_BiasesArray( TwoTensors.filtersTensor4d_biasesTensor3d( ReturnOrClone_Activation.Base ) ) {

  /**
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, nActivationId, nPassThroughStyleId,
    nHigherHalfDifferent, inputChannelCount_lowerHalf ) {

    super(
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, nActivationId, nPassThroughStyleId,
      nHigherHalfDifferent, inputChannelCount_lowerHalf );

  }

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpreted as weights.
   *
   * @param {ActivationEscaping.ScaleBoundsArray} inputScaleBoundsArray
   *   The element value bounds (per channel) of input. Usually, it is The .output of the previous convolution-bias-activation value bounds
   * set of this depthwise convolution. It will be kept (not cloned) directly. So caller should not modify them.
   *
   * @return {boolean} Return true, if succeeded.
   */
  init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray ) {

    // Q1: Why is the inputFloat32Array not a parameter of constructor?
    // A1: The reason is to avoid keeping it as this.inputFloat32Array so that it could be released by memory garbage collector.
    //
    // Q2: Why are not filtersWeights and biasesWeights kept in this?
    // A2: So that inputFloat32Array could be released.

    this.disposeTensors();

    // 1.

    // 1.1 Determine operation functions.
    Base.Setup_bDepthwise_pfn.call( this );

    let bExtractOk;
    if ( !this.bDepthwise ) {
      bExtractOk = true; // 2. no operation at all. No depthwise (e.g. zero or negative number) (so no channel multiplier, too).

      this.byteOffsetBegin = this.byteOffsetEnd = byteOffsetBegin;
      this.tensorWeightCountExtracted = this.tensorWeightCountTotal = 0;

      this.boundsArraySet = new BoundsArraySet.Depthwise( inputScaleBoundsArray, inputScaleBoundsArray.channelCount );
      this.boundsArraySet.output0.set_all_byScaleBoundsArray( inputScaleBoundsArray ); // Bypass previous to next.

    } else {

      bExtractOk = super.init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray );
      if ( bExtractOk ) {
        try {
          if ( this.filtersShape && this.filtersArray ) {
            this.filtersTensor4d = tf.tensor( this.filtersArray, this.filtersShape );
            this.filtersArray = null; // Release for reducing memory usage.
          }

          if ( this.biasesShape && this.biasesArray ) {
            this.biasesTensor3d = tf.tensor( this.biasesArray, this.biasesShape );
            this.biasesArray = null; // Release for reducing memory usage.
          }

        } catch ( e ) {  // If failed (e.g. memory not enough), return false.
          bExtractOk = false;
        }
      }

    }

    this.bInitOk = bExtractOk;
    return this.bInitOk;
  }

  /** Release tensors.
   */
  disposeTensors() {
    super.disposeTensors(); // Release filtersTensor4d and biasesTensor3d.

    this.pfnOperationBiasActivation = this.pfnOperation = this.pfnActivation = null;

    // If these properties does not exist, assigning value (even undefined) to them will create them. Avoid it.
    {
      if ( this.bDepthwiseAvg )
        this.bDepthwiseAvg = undefined;

      if ( this.bDepthwiseMax )
        this.bDepthwiseMax = undefined;

      if ( this.bDepthwiseConv )
        this.bDepthwiseConv = undefined;
    }

    this.bDepthwise = false;
    this.byteOffsetEnd = -1;
    this.bKeepInputTensor = false;  // Default will dispose input tensor.
    this.bInitOk = false;
  }

  /**
   * Adjust this.pfnOperation (and this.pfnOperationBiasActivation if need) so that this.pfnOperation() and this.pfnOperationBiasActivation()
   * will or will not dispose its inputTensor.
   */
  setKeepInputTensor( bKeepInputTensor ) {
    if ( bKeepInputTensor == this.bKeepInputTensor )
      return;

    this.bKeepInputTensor = bKeepInputTensor;
    if ( bKeepInputTensor ) {

      switch ( this.pfnOperation ) {

        // Just clone input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: pfnOperationBiasActivation should not be changed here because there might be bias and activation.
        case Base.return_input_directly: this.pfnOperation = Base.keep_input_return_copy; break;

        case Base.Avg_and_destroy:       this.pfnOperation = Base.Avg_and_keep;  break;
        case Base.Max_and_destroy:       this.pfnOperation = Base.Max_and_keep;  break;
        case Base.Conv_and_destroy:      this.pfnOperation = Base.Conv_and_keep; break;

        // Just clone input if unknown depthwise operation.
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        default:                         this.pfnOperation = this.pfnOperationBiasActivation = Base.keep_input_return_copy;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    } else {

      switch ( this.pfnOperation ) {

        // Just return input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: pfnOperationBiasActivation should not be changed here because there might be bias and activation.
        case Base.keep_input_return_copy: this.pfnOperation = Base.return_input_directly; break;

        case Base.Avg_and_keep:           this.pfnOperation = Base.Avg_and_destroy;  break;
        case Base.Max_and_keep:           this.pfnOperation = Base.Max_and_destroy;  break;
        case Base.Conv_and_keep:          this.pfnOperation = Base.Conv_and_destroy; break;

        // Just return input if unknown depthwise operation.
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        default:                          this.pfnOperation = this.pfnOperationBiasActivation = Base.return_input_directly;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    }
  }

  get bExisted() {
    return this.bDepthwise;
  }

  /** Determine this.bDepthwiseXxx and this.pfnXxx data members.
   *
   * @param {Base} this
   *   The Base object to be determined and modified.
   */
  static Setup_bDepthwise_pfn() {

    // 1.
    if ( this.AvgMax_Or_ChannelMultiplier < 0 ) { // Depthwise by AVG or MAX pooling (so no channel multiplier).

      // if 1x1 AVG pooling ( and strides is 1 ), or 1x1 MAX pooling ( and strides is 1 ), or illegal pooling type (i.e. not AVG, not MAX):
      //   - As no depthwise operation (i.e. ( this.bDepthwise == false ) )
      //   - Just return input (i.e. ( this.pfnOperation == Base.return_input_directly ) )

      // When 1x1 AVG or MAX pooling (and strides is 1), the result of depthwise operation (not include bias and activation) is the same as input.
      let bOperationResultSameAsInput = ( ( 1 == this.filterHeight ) && ( 1 == this.filterWidth ) && ( 1 == this.strides ) );

      switch ( this.AvgMax_Or_ChannelMultiplier ) {
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG:
          this.bDepthwise = this.bDepthwiseAvg = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Avg_and_destroy;
          break;

        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX:
          this.bDepthwise = this.bDepthwiseMax = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Max_and_destroy;
          break;
      }

    } else if ( this.AvgMax_Or_ChannelMultiplier >= 1 ) { // Depthwise by convolution (with channel multiplier).
      this.bDepthwise = this.bDepthwiseConv = true;

      this.pfnOperation = Base.Conv_and_destroy; // will dispose inputTensor.

    } else { // No depthwise (e.g. zero or negative number) (so no channel multiplier).
      this.bDepthwise = false;
    }

    // 2.
    this.pfnActivation = Base.ActivationFunction_getById( this.nActivationId );

    // 3.
    if ( this.bDepthwise ) {
      if ( this.bBias ) {
        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationBiasActivation_and_destroy_or_keep;
        else
          this.pfnOperationBiasActivation = Base.OperationBias_and_destroy_or_keep;
      } else {
        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationActivation_and_destroy_or_keep;
         else
          this.pfnOperationBiasActivation = Base.Operation_and_destroy_or_keep;
      }
    } else {
      // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
      this.pfnOperationBiasActivation = this.pfnOperation = Base.return_input_directly;
    }
  }

  /** Depthwise Average Pooling. */
  static Avg_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
  }

  static Avg_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Max Pooling. */
  static Max_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
  }

  static Max_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Convolution. */
  static Conv_and_keep( inputTensor ) {
    return tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
  }

  static Conv_and_destroy( inputTensor ) {
    let t = tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Operation, Bias and Activation. */
  static Operation_and_destroy_or_keep( inputTensor ) {
    return this.pfnOperation( inputTensor ); // may destroy or keep.
  }

  static OperationBias_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    return t1;
  }

  static OperationActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = this.pfnActivation( t0 );
    t0.dispose();

    return t1;
  }

  static OperationBiasActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    t0 = this.pfnActivation( t1 );
    t1.dispose();

    return t0;
  }

}
<commit_msg>Update Depthwise_Base.js<commit_after>export { Base };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as TwoTensors from "../../util/TwoTensors.js";
import * as ReturnOrClone_Activation from "../ReturnOrClone_Activation.js";
import * as BoundsArraySet from "../BoundsArraySet.js";
import { FiltersArray_BiasesArray } from "./Depthwise_FiltersArray_BiasesArray.js";

/**
 * Handle depthwise convolution, bias and activation.
 *
 * @member {boolean} bExisted
 *   If true, this depthwise operation exists. The same as this.bDepthwise.
 *
 * @member {boolean} bDepthwise
 *   If true, this depthwise operation exists. The same as this.bExisted.
 *
 * @member {boolean} bDepthwiseAvg
 *   If true, this depthwise operation exists. And it is depthwise average pooling.
 *
 * @member {boolean} bDepthwiseMax
 *   If true, this depthwise operation exists. And it is depthwise maximum pooling.
 *
 * @member {boolean} bDepthwiseConv
 *   If true, this depthwise operation exist. And it is depthwise convolution.
 *
 * @member {boolean} bInitOk
 *   If true, the init() is successful.
 *
 * @member {function} pfnOperation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensor may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Avg_and_destroy(), Avg_and_keep(), Max_and_destroy(), Max_and_keep(),
 * Conv_and_destroy(), Conv_and_keep() according to the parameters.
 *
 * @member {function} pfnOperationBiasActivation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensors may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Operation_and_destroy_or_keep(), OperationBias_and_destroy_or_keep(),
 * OperationActivation_and_destroy_or_keep(), OperationBiasActivation_and_destroy_or_keep() according to the parameters.
 *
 * @see FiltersArray_BiasesArray
 */
class Base extends FiltersArray_BiasesArray( TwoTensors.filtersTensor4d_biasesTensor3d( ReturnOrClone_Activation.Base ) ) {

  /**
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, nActivationId, nPassThroughStyleId,
    nHigherHalfDifferent, inputChannelCount_lowerHalf ) {

    super(
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, nActivationId, nPassThroughStyleId,
      nHigherHalfDifferent, inputChannelCount_lowerHalf );

  }

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpreted as weights.
   *
   * @param {ActivationEscaping.ScaleBoundsArray} inputScaleBoundsArray
   *   The element value bounds (per channel) of input. Usually, it is The .output of the previous convolution-bias-activation value bounds
   * set of this depthwise convolution. It will be kept (not cloned) directly. So caller should not modify them.
   *
   * @return {boolean} Return true, if succeeded.
   */
  init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray ) {

    // Q1: Why is the inputFloat32Array not a parameter of constructor?
    // A1: The reason is to avoid keeping it as this.inputFloat32Array so that it could be released by memory garbage collector.
    //
    // Q2: Why are not filtersWeights and biasesWeights kept in this?
    // A2: So that inputFloat32Array could be released.

    this.disposeTensors();

    // 1.

    // 1.1 Determine operation functions.
    Base.Setup_bDepthwise_pfn.call( this );

    let bExtractOk;
    if ( !this.bDepthwise ) {
      bExtractOk = true; // 2. no operation at all. No depthwise (e.g. zero or negative number) (so no channel multiplier, too).

      this.byteOffsetBegin = this.byteOffsetEnd = byteOffsetBegin;
      this.tensorWeightCountExtracted = this.tensorWeightCountTotal = 0;

      this.boundsArraySet = new BoundsArraySet.Depthwise( inputScaleBoundsArray, inputScaleBoundsArray.channelCount );
      this.boundsArraySet.output0.set_all_byScaleBoundsArray( inputScaleBoundsArray ); // Bypass previous to next.

    } else { // 3.

      bExtractOk = super.init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray );
      if ( bExtractOk ) {
        try {
          if ( this.filtersShape && this.filtersArray ) {
            this.filtersTensor4d = tf.tensor( this.filtersArray, this.filtersShape );
            this.filtersArray = null; // Release for reducing memory usage.
          }

          if ( this.biasesShape && this.biasesArray ) {
            this.biasesTensor3d = tf.tensor( this.biasesArray, this.biasesShape );
            this.biasesArray = null; // Release for reducing memory usage.
          }

        } catch ( e ) {  // If failed (e.g. memory not enough), return false.
          bExtractOk = false;
        }
      }

    }

    this.bInitOk = bExtractOk;
    return this.bInitOk;
  }

  /** Release tensors.
   */
  disposeTensors() {
    super.disposeTensors(); // Release filtersTensor4d and biasesTensor3d.

    this.pfnOperationBiasActivation = this.pfnOperation = this.pfnActivation = null;

    // If these properties does not exist, assigning value (even undefined) to them will create them. Avoid it.
    {
      if ( this.bDepthwiseAvg )
        this.bDepthwiseAvg = undefined;

      if ( this.bDepthwiseMax )
        this.bDepthwiseMax = undefined;

      if ( this.bDepthwiseConv )
        this.bDepthwiseConv = undefined;
    }

    this.bDepthwise = false;
    this.byteOffsetEnd = -1;
    this.bKeepInputTensor = false;  // Default will dispose input tensor.
    this.bInitOk = false;
  }

  /**
   * Adjust this.pfnOperation (and this.pfnOperationBiasActivation if need) so that this.pfnOperation() and this.pfnOperationBiasActivation()
   * will or will not dispose its inputTensor.
   */
  setKeepInputTensor( bKeepInputTensor ) {
    if ( bKeepInputTensor == this.bKeepInputTensor )
      return;

    this.bKeepInputTensor = bKeepInputTensor;
    if ( bKeepInputTensor ) {

      switch ( this.pfnOperation ) {

        // Just clone input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: pfnOperationBiasActivation should not be changed here because there might be bias and activation.
        case Base.return_input_directly: this.pfnOperation = Base.keep_input_return_copy; break;

        case Base.Avg_and_destroy:       this.pfnOperation = Base.Avg_and_keep;  break;
        case Base.Max_and_destroy:       this.pfnOperation = Base.Max_and_keep;  break;
        case Base.Conv_and_destroy:      this.pfnOperation = Base.Conv_and_keep; break;

        // Just clone input if unknown depthwise operation.
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        default:                         this.pfnOperation = this.pfnOperationBiasActivation = Base.keep_input_return_copy;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    } else {

      switch ( this.pfnOperation ) {

        // Just return input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: pfnOperationBiasActivation should not be changed here because there might be bias and activation.
        case Base.keep_input_return_copy: this.pfnOperation = Base.return_input_directly; break;

        case Base.Avg_and_keep:           this.pfnOperation = Base.Avg_and_destroy;  break;
        case Base.Max_and_keep:           this.pfnOperation = Base.Max_and_destroy;  break;
        case Base.Conv_and_keep:          this.pfnOperation = Base.Conv_and_destroy; break;

        // Just return input if unknown depthwise operation.
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        default:                          this.pfnOperation = this.pfnOperationBiasActivation = Base.return_input_directly;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    }
  }

  get bExisted() {
    return this.bDepthwise;
  }

  /** Determine this.bDepthwiseXxx and this.pfnXxx data members.
   *
   * @param {Base} this
   *   The Base object to be determined and modified.
   */
  static Setup_bDepthwise_pfn() {

    // 1.
    if ( this.AvgMax_Or_ChannelMultiplier < 0 ) { // Depthwise by AVG or MAX pooling (so no channel multiplier).

      // if 1x1 AVG pooling ( and strides is 1 ), or 1x1 MAX pooling ( and strides is 1 ), or illegal pooling type (i.e. not AVG, not MAX):
      //   - As no depthwise operation (i.e. ( this.bDepthwise == false ) )
      //   - Just return input (i.e. ( this.pfnOperation == Base.return_input_directly ) )

      // When 1x1 AVG or MAX pooling (and strides is 1), the result of depthwise operation (not include bias and activation) is the same as input.
      let bOperationResultSameAsInput = ( ( 1 == this.filterHeight ) && ( 1 == this.filterWidth ) && ( 1 == this.strides ) );

      switch ( this.AvgMax_Or_ChannelMultiplier ) {
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG:
          this.bDepthwise = this.bDepthwiseAvg = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Avg_and_destroy;
          break;

        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX:
          this.bDepthwise = this.bDepthwiseMax = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Max_and_destroy;
          break;
      }

    } else if ( this.AvgMax_Or_ChannelMultiplier >= 1 ) { // Depthwise by convolution (with channel multiplier).
      this.bDepthwise = this.bDepthwiseConv = true;

      this.pfnOperation = Base.Conv_and_destroy; // will dispose inputTensor.

    } else { // No depthwise (e.g. zero or negative number) (so no channel multiplier).
      this.bDepthwise = false;
    }

    // 2.
    this.pfnActivation = Base.ActivationFunction_getById( this.nActivationId );

    // 3.
    if ( this.bDepthwise ) {
      if ( this.bBias ) {
        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationBiasActivation_and_destroy_or_keep;
        else
          this.pfnOperationBiasActivation = Base.OperationBias_and_destroy_or_keep;
      } else {
        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationActivation_and_destroy_or_keep;
         else
          this.pfnOperationBiasActivation = Base.Operation_and_destroy_or_keep;
      }
    } else {
      // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
      this.pfnOperationBiasActivation = this.pfnOperation = Base.return_input_directly;
    }
  }

  /** Depthwise Average Pooling. */
  static Avg_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
  }

  static Avg_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Max Pooling. */
  static Max_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
  }

  static Max_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Convolution. */
  static Conv_and_keep( inputTensor ) {
    return tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
  }

  static Conv_and_destroy( inputTensor ) {
    let t = tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Operation, Bias and Activation. */
  static Operation_and_destroy_or_keep( inputTensor ) {
    return this.pfnOperation( inputTensor ); // may destroy or keep.
  }

  static OperationBias_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    return t1;
  }

  static OperationActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = this.pfnActivation( t0 );
    t0.dispose();

    return t1;
  }

  static OperationBiasActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    t0 = this.pfnActivation( t1 );
    t1.dispose();

    return t0;
  }

}
<|endoftext|>