<commit_before>//
//  main.cpp
//  loadcaffe
//
//  Created by Sergey Zagoruyko on 28/11/14.
//  Copyright (c) 2014 Sergey Zagoruyko. All rights reserved.
//

#include <iostream>
#include <fstream>
#include <fcntl.h>
#include <unistd.h>

#include <TH/TH.h>

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>

#include "build/caffe.pb.h"

using google::protobuf::io::FileInputStream;
using google::protobuf::io::FileOutputStream;
using google::protobuf::io::ZeroCopyInputStream;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::ZeroCopyOutputStream;
using google::protobuf::io::CodedOutputStream;
using google::protobuf::Message;

extern "C" {
void loadBinary(void** handle, const char* prototxt_name, const char* binary_name);
void convertProtoToLua(void** handle, const char* lua_name, const char* cuda_package);
void loadModule(const void** handle, const char* name, THFloatTensor* weight, THFloatTensor* bias);
void destroyBinary(void** handle);
}

bool ReadProtoFromTextFile(const char* filename, Message* proto) {
    int fd = open(filename, O_RDONLY);
    if(fd == 0)
      return false;

    FileInputStream* input = new FileInputStream(fd);
    bool success = google::protobuf::TextFormat::Parse(input, proto);
    delete input;
    close(fd);
    return success;
}

bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {
    int fd = open(filename, O_RDONLY);
    if(fd == 0)
      return false;

    ZeroCopyInputStream* raw_input = new FileInputStream(fd);
    CodedInputStream* coded_input = new CodedInputStream(raw_input);
    coded_input->SetTotalBytesLimit(1073741824, 536870912);

    bool success = proto->ParseFromCodedStream(coded_input);

    delete coded_input;
    delete raw_input;
    close(fd);
    return success;
}

void convertProtoToLua(void** handle, const char* lua_name, const char* cuda_package)
{
  const caffe::NetParameter netparam = *(const caffe::NetParameter*)handle[1];

  std::ofstream ofs (lua_name);

  ofs << "require '" << cuda_package << "'\n";
  ofs << "require 'cunn'\n";
  ofs << "model = {}\n";

  int num_output = netparam.input_dim_size();
    for (int i=0; i<netparam.layers_size(); ++i)
    {
	char buf[1024];
	bool success = true;
        bool insert_view = false;
        switch(netparam.layers(i).type())
        {
            case caffe::LayerParameter::CONVOLUTION:
            {
                auto &param = netparam.layers(i).convolution_param();
                int groups = param.group() == 0 ? 1 : param.group();
                int nInputPlane = netparam.layers(i).blobs(0).channels()*groups;
                int nOutputPlane = param.num_output();
                num_output = nOutputPlane;
                int kW = param.kernel_w();
                int kH = param.kernel_h();
                int dW = param.stride_w();
                int dH = param.stride_h();
                if(kW==0 || kH==0)
                {
                  kW = param.kernel_size();
                  kH = kW;
                }
                if(dW==0 || dH==0)
                {
                  dW = param.stride();
                  dH = dW;
                }
                int padding = param.pad();
                if(std::string(cuda_package) == "ccn2")
                  sprintf(buf, "ccn2.SpatialConvolution(%d, %d, %d, %d, %d, %d)", nInputPlane, nOutputPlane, kW, dW, padding, groups);
                else
                  sprintf(buf, "%s.SpatialConvolution%s(%d, %d, %d, %d, %d, %d, %d)", cuda_package, std::string(cuda_package)=="nn" ? "MM" : "", nInputPlane, nOutputPlane, kW, kH, dW, dH, padding);
                break;
            }
            case caffe::LayerParameter::POOLING:
            {
                auto &param = netparam.layers(i).pooling_param();
                std::string ptype = param.pool() == caffe::PoolingParameter::MAX ? "Max" : "Avg";
                int kW = param.kernel_w();
                int kH = param.kernel_h();
                int dW = param.stride_w();
                int dH = param.stride_h();
                if(kW==0 || kH==0)
                {
                  kW = param.kernel_size();
                  kH = kW;
                }
                if(dW==0 || dH==0)
                {
                  dW = param.stride()==0;
                  dH = dW;
                }
                if(std::string(cuda_package) == "ccn2")
                  sprintf(buf, "ccn2.Spatial%sPooling(%d, %d)", ptype.c_str(), kW, dW);
                else
                  sprintf(buf, "%s.Spatial%sPooling(%d, %d, %d, %d)", cuda_package, ptype=="Avg" ? "Average" : "Max", kW, kH, dW, dH);
                break;
            }
            case caffe::LayerParameter::RELU:
            {
	        sprintf(buf, "nn.ReLU()");
                break;
            }
            case caffe::LayerParameter::LRN:
            {
              if(std::string(cuda_package) == "ccn2")
              {
                auto &param = netparam.layers(i).lrn_param();
                int local_size = param.local_size();
                float alpha = param.alpha();
                float beta = param.beta();
                sprintf(buf, "ccn2.SpatialCrossResponseNormalization(%d, %.6f, %.4f)", local_size, alpha, beta);
              }
              else
              {
                success = false;
              }
              break;
            }
            case caffe::LayerParameter::INNER_PRODUCT:
            {
                auto &param = netparam.layers(i).inner_product_param();
                int nInputPlane = netparam.layers(i).blobs(0).width();
                int nOutputPlane = param.num_output();
                insert_view = num_output != nInputPlane;
                num_output = nOutputPlane;
                sprintf(buf, "nn.Linear(%d, %d)", nInputPlane, nOutputPlane);
                break;
            }
            case caffe::LayerParameter::DROPOUT:
            {
                sprintf(buf, "nn.Dropout(%f)", netparam.layers(i).dropout_param().dropout_ratio());
                break;
            }
            case caffe::LayerParameter::SOFTMAX_LOSS:
            {
	        sprintf(buf, "nn.SoftMax()");
                break;
            }
            default:
            {
                std::cout << "MODULE " << netparam.layers(i).type() << " UNDEFINED\n";
		success = false;
            }
        }

        if(insert_view)
        {
          if(std::string(cuda_package) == "ccn2")
            ofs << "table.insert(model, {'reshape', nn.Transpose({4,1},{4,2},{4,3})})\n";
          ofs << "table.insert(model, {'view', nn.View(-1):setNumInputDims(3)})\n";
        }
	if(success)
	  ofs << "table.insert(model, {\"" << netparam.layers(i).name() << "\", " << buf << "})\n";
	else
	  ofs << "-- module \"" << netparam.layers(i).name() << "\" not found\n";
    }
}

void loadBinary(void** handle, const char* prototxt_name, const char* binary_name)
{
  caffe::NetParameter* netparam = (caffe::NetParameter*)handle;
  netparam = new caffe::NetParameter();
  ReadProtoFromTextFile(prototxt_name, netparam);
  bool success = ReadProtoFromBinaryFile(binary_name, netparam);
  if(success)
    std::cout << "Successfully loaded " << binary_name << std::endl;
  else
    std::cout << "Couldn't load " << binary_name << std::endl;

  handle[1] = netparam;
}

void destroyBinary(void** handle)
{
  const caffe::NetParameter* netparam2 = (const caffe::NetParameter*)handle[1];
  delete netparam2;
}

void loadModule(const void** handle, const char* name, THFloatTensor* weight, THFloatTensor* bias)
{
  if(handle == NULL)
  {
    std::cout << "network not loaded!\n";
    return;
  }

  const caffe::NetParameter* netparam = (const caffe::NetParameter*)handle[1];

  int n = netparam->layers_size();
  for(int i=0; i<n; ++i)
  {
    auto &layer = netparam->layers(i);
    if(std::string(name) == layer.name())
    {
      int nInputPlane = layer.blobs(0).channels();
      int nOutputPlane = layer.blobs(0).num();
      int kW = layer.blobs(0).width();
      int kH = layer.blobs(0).height();
      printf("%s: %d %d %d %d\n", name, nOutputPlane, nInputPlane, kW, kH);

      THFloatTensor_resize4d(weight, nOutputPlane, nInputPlane, kW, kH);
      memcpy(THFloatTensor_data(weight), layer.blobs(0).data().data(), sizeof(float)*nOutputPlane*nInputPlane*kW*kH);

      THFloatTensor_resize1d(bias, layer.blobs(1).data_size());
      memcpy(THFloatTensor_data(bias), layer.blobs(1).data().data(), sizeof(float)*layer.blobs(1).data_size());
    }
  }
}
<commit_msg>added softmax<commit_after>//
//  main.cpp
//  loadcaffe
//
//  Created by Sergey Zagoruyko on 28/11/14.
//  Copyright (c) 2014 Sergey Zagoruyko. All rights reserved.
//

#include <iostream>
#include <fstream>
#include <fcntl.h>
#include <unistd.h>

#include <TH/TH.h>

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>

#include "build/caffe.pb.h"

using google::protobuf::io::FileInputStream;
using google::protobuf::io::FileOutputStream;
using google::protobuf::io::ZeroCopyInputStream;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::ZeroCopyOutputStream;
using google::protobuf::io::CodedOutputStream;
using google::protobuf::Message;

extern "C" {
void loadBinary(void** handle, const char* prototxt_name, const char* binary_name);
void convertProtoToLua(void** handle, const char* lua_name, const char* cuda_package);
void loadModule(const void** handle, const char* name, THFloatTensor* weight, THFloatTensor* bias);
void destroyBinary(void** handle);
}

bool ReadProtoFromTextFile(const char* filename, Message* proto) {
    int fd = open(filename, O_RDONLY);
    if(fd == 0)
      return false;

    FileInputStream* input = new FileInputStream(fd);
    bool success = google::protobuf::TextFormat::Parse(input, proto);
    delete input;
    close(fd);
    return success;
}

bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {
    int fd = open(filename, O_RDONLY);
    if(fd == 0)
      return false;

    ZeroCopyInputStream* raw_input = new FileInputStream(fd);
    CodedInputStream* coded_input = new CodedInputStream(raw_input);
    coded_input->SetTotalBytesLimit(1073741824, 536870912);

    bool success = proto->ParseFromCodedStream(coded_input);

    delete coded_input;
    delete raw_input;
    close(fd);
    return success;
}

void convertProtoToLua(void** handle, const char* lua_name, const char* cuda_package)
{
  const caffe::NetParameter netparam = *(const caffe::NetParameter*)handle[1];

  std::ofstream ofs (lua_name);

  ofs << "require '" << cuda_package << "'\n";
  ofs << "require 'cunn'\n";
  ofs << "model = {}\n";
  if(std::string(cuda_package)=="ccn2")
    ofs<< "table.insert(model, {'tr1', nn.Transpose({1,4},{1,3},{1,2})})\n";

  int num_output = netparam.input_dim_size();
    for (int i=0; i<netparam.layers_size(); ++i)
    {
	char buf[1024];
	bool success = true;
        bool insert_view = false;
        switch(netparam.layers(i).type())
        {
            case caffe::LayerParameter::CONVOLUTION:
            {
                auto &param = netparam.layers(i).convolution_param();
                int groups = param.group() == 0 ? 1 : param.group();
                int nInputPlane = netparam.layers(i).blobs(0).channels()*groups;
                int nOutputPlane = param.num_output();
                num_output = nOutputPlane;
                int kW = param.kernel_w();
                int kH = param.kernel_h();
                int dW = param.stride_w();
                int dH = param.stride_h();
                if(kW==0 || kH==0)
                {
                  kW = param.kernel_size();
                  kH = kW;
                }
                if(dW==0 || dH==0)
                {
                  dW = param.stride();
                  dH = dW;
                }
                int padding = param.pad();
                if(std::string(cuda_package) == "ccn2")
                  sprintf(buf, "ccn2.SpatialConvolution(%d, %d, %d, %d, %d, %d)", nInputPlane, nOutputPlane, kW, dW, padding, groups);
                else
                  sprintf(buf, "%s.SpatialConvolution%s(%d, %d, %d, %d, %d, %d, %d)", cuda_package, std::string(cuda_package)=="nn" ? "MM" : "", nInputPlane, nOutputPlane, kW, kH, dW, dH, padding);
                break;
            }
            case caffe::LayerParameter::POOLING:
            {
                auto &param = netparam.layers(i).pooling_param();
                std::string ptype = param.pool() == caffe::PoolingParameter::MAX ? "Max" : "Avg";
                int kW = param.kernel_w();
                int kH = param.kernel_h();
                int dW = param.stride_w();
                int dH = param.stride_h();
                if(kW==0 || kH==0)
                {
                  kW = param.kernel_size();
                  kH = kW;
                }
                if(dW==0 || dH==0)
                {
                  dW = param.stride();
                  dH = dW;
                }
                if(std::string(cuda_package) == "ccn2")
                  sprintf(buf, "ccn2.Spatial%sPooling(%d, %d)", ptype.c_str(), kW, dW);
                else
                  sprintf(buf, "%s.Spatial%sPooling(%d, %d, %d, %d)", cuda_package, ptype=="Avg" ? "Average" : "Max", kW, kH, dW, dH);
                break;
            }
            case caffe::LayerParameter::RELU:
            {
	        sprintf(buf, "nn.ReLU()");
                break;
            }
            case caffe::LayerParameter::LRN:
            {
              if(std::string(cuda_package) == "ccn2")
              {
                auto &param = netparam.layers(i).lrn_param();
                int local_size = param.local_size();
                float alpha = param.alpha();
                float beta = param.beta();
                sprintf(buf, "ccn2.SpatialCrossResponseNormalization(%d, %.6f, %.4f)", local_size, alpha, beta);
              }
              else
              {
                success = false;
              }
              break;
            }
            case caffe::LayerParameter::INNER_PRODUCT:
            {
                auto &param = netparam.layers(i).inner_product_param();
                int nInputPlane = netparam.layers(i).blobs(0).width();
                int nOutputPlane = param.num_output();
                insert_view = num_output != nInputPlane;
                num_output = nOutputPlane;
                sprintf(buf, "nn.Linear(%d, %d)", nInputPlane, nOutputPlane);
                break;
            }
            case caffe::LayerParameter::DROPOUT:
            {
                sprintf(buf, "nn.Dropout(%f)", netparam.layers(i).dropout_param().dropout_ratio());
                break;
            }
            case caffe::LayerParameter::SOFTMAX_LOSS:
            {
	        sprintf(buf, "nn.SoftMax()");
                break;
            }
            case caffe::LayerParameter::SOFTMAX:
            {
	        sprintf(buf, "nn.SoftMax()");
                break;
            }
            default:
            {
                std::cout << "MODULE " << netparam.layers(i).name() << " UNDEFINED\n";
		success = false;
            }
        }

        if(insert_view)
        {
          if(std::string(cuda_package) == "ccn2")
            ofs << "table.insert(model, {'reshape', nn.Transpose({4,1},{4,2},{4,3})})\n";
          ofs << "table.insert(model, {'view', nn.View(-1):setNumInputDims(3)})\n";
        }
	if(success)
	  ofs << "table.insert(model, {\"" << netparam.layers(i).name() << "\", " << buf << "})\n";
	else
	  ofs << "-- module \"" << netparam.layers(i).name() << "\" not found\n";
    }
}

void loadBinary(void** handle, const char* prototxt_name, const char* binary_name)
{
  caffe::NetParameter* netparam = (caffe::NetParameter*)handle;
  netparam = new caffe::NetParameter();
  ReadProtoFromTextFile(prototxt_name, netparam);
  bool success = ReadProtoFromBinaryFile(binary_name, netparam);
  if(success)
    std::cout << "Successfully loaded " << binary_name << std::endl;
  else
    std::cout << "Couldn't load " << binary_name << std::endl;

  handle[1] = netparam;
}

void destroyBinary(void** handle)
{
  const caffe::NetParameter* netparam2 = (const caffe::NetParameter*)handle[1];
  delete netparam2;
}

void loadModule(const void** handle, const char* name, THFloatTensor* weight, THFloatTensor* bias)
{
  if(handle == NULL)
  {
    std::cout << "network not loaded!\n";
    return;
  }

  const caffe::NetParameter* netparam = (const caffe::NetParameter*)handle[1];

  int n = netparam->layers_size();
  for(int i=0; i<n; ++i)
  {
    auto &layer = netparam->layers(i);
    if(std::string(name) == layer.name())
    {
      int nInputPlane = layer.blobs(0).channels();
      int nOutputPlane = layer.blobs(0).num();
      int kW = layer.blobs(0).width();
      int kH = layer.blobs(0).height();
      printf("%s: %d %d %d %d\n", name, nOutputPlane, nInputPlane, kW, kH);

      THFloatTensor_resize4d(weight, nOutputPlane, nInputPlane, kW, kH);
      memcpy(THFloatTensor_data(weight), layer.blobs(0).data().data(), sizeof(float)*nOutputPlane*nInputPlane*kW*kH);

      THFloatTensor_resize1d(bias, layer.blobs(1).data_size());
      memcpy(THFloatTensor_data(bias), layer.blobs(1).data().data(), sizeof(float)*layer.blobs(1).data_size());
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2021 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <regex>
#include <deconz/dbg_trace.h>

#include "product_match.h"
#include "resource.h"

/*! The product map is a helper to map Basic Cluster manufacturer name and modelid
   to human readable product identifiers like marketing string or the model no. as printed on the product package.

   In case of Tuya multiple entries may refer to the same device, so in matching code
   it's best to match against the \c productId.

   Example:

   if (R_GetProductId(sensor) == QLatin1String("SEA801-ZIGBEE TRV"))
   {
   }

   Note: this will later on be replaced with the data from DDF files.
*/
struct ProductMap
{
    const char *zmanufacturerName;
    const char *zmodelId;
    const char *manufacturer;
    // a common product identifier even if multipe branded versions exist
    const char *commonProductId;
};

static const ProductMap products[] =
{
    // Prefix signification
    // --------------------
    // Tuya_THD : thermostat device using Tuya cluster
    // Tuya_COVD : covering device using Tuya cluster
    // Tuya_RPT : Repeater
    // Tuya_SEN : Sensor

    // Tuya Thermostat / TRV
    {"_TYST11_zuhszj9s", "uhszj9s", "HiHome", "Tuya_THD WZB-TRVL TRV"},
    {"_TYST11_KGbxAXL2", "GbxAXL2", "Saswell", "Tuya_THD SEA801-ZIGBEE TRV"},
    {"_TYST11_c88teujp", "88teujp", "Saswell", "Tuya_THD SEA801-ZIGBEE TRV"},
    {"_TZE200_c88teujp", "TS0601", "Saswell", "Tuya_THD SEA801-ZIGBEE TRV"},
    {"_TYST11_ckud7u2l", "kud7u2l", "Tuya", "Tuya_THD HY369 TRV"},
    {"_TZE200_ckud7u2l", "TS0601", "Tuya", "Tuya_THD HY369 TRV"},
    {"_TZE200_ywdxldoj", "TS0601", "MOES/tuya", "Tuya_THD HY368 TRV"},
    {"_TZE200_fhn3negr", "TS0601", "MOES/tuya", "Tuya_THD MOES TRV"},
    {"_TZE200_aoclfnxz", "TS0601", "Moes", "Tuya_THD BTH-002 Thermostat"},
    {"_TYST11_jeaxp72v", "eaxp72v", "Essentials", "Tuya_THD Essentials TRV"},
    {"_TYST11_kfvq6avy", "fvq6avy", "Revolt", "Tuya_THD NX-4911-675 TRV"},
    {"_TZE200_kfvq6avy", "TS0601", "Revolt", "Tuya_THD NX-4911-675 TRV"},
    {"_TYST11_zivfvd7h", "ivfvd7h", "Siterwell", "Tuya_THD GS361A-H04 TRV"},
    {"_TZE200_zivfvd7h", "TS0601", "Siterwell", "Tuya_THD GS361A-H04 TRV"},
    {"_TYST11_yw7cahqs", "w7cahqs", "Hama", "Tuya_THD Smart radiator TRV"},
    {"_TZE200_yw7cahqs", "TS0601", "Hama", "Tuya_THD Smart radiator TRV"},
    {"_TZE200_cwnjrr72", "TS0601", "MOES", "Tuya_THD HY368 TRV"},

    // Tuya Covering
    {"_TYST11_wmcdj3aq", "mcdj3aq", "Zemismart", "Tuya_COVD ZM25TQ"},
    {"_TZE200_wmcdj3aq", "TS0601", "Zemismart", "Tuya_COVD ZM25TQ"},
    {"_TZE200_fzo2pocs", "TS0601", "Zemismart", "Tuya_COVD ZM25TQ"},
    {"_TYST11_xu1rkty3", "u1rkty3", "Smart Home", "Tuya_COVD DT82LEMA-1.2N"},
    {"_TZE200_xuzcvlku", "TS0601", "Zemismart", "Tuya_COVD M515EGB"},
    {"_TZE200_rddyvrci", "TS0601", "Moes", "Tuya_COVD AM43-0.45/40-ES-EZ(TY)"},
    {"_TZE200_zah67ekd", "TS0601", "MoesHouse / Livolo", "Tuya_COVD AM43-0.45-40"},
    {"_TZE200_nogaemzt", "TS0601", "Tuya", "Tuya_COVD YS-MT750"},
    {"_TZE200_zpzndjez", "TS0601", "Tuya", "Tuya_COVD DS82"},
    {"_TZE200_cowvfni3", "TS0601", "Zemismart", "Tuya_COVD ZM79E-DT"},
    {"_TZE200_5zbp6j0u", "TS0601", "Tuya/Zemismart", "Tuya_COVD DT82LEMA-1.2N"},
    {"_TZE200_fdtjuw7u", "TS0601", "Yushun", "Tuya_COVD YS-MT750"},
    {"_TZE200_bqcqqjpb", "TS0601", "Yushun", "Tuya_COVD YS-MT750"},
    {"_TZE200_nueqqe6k", "TS0601", "Zemismart", "Tuya_COVD M515EGB"},

    // Tuya covering not using tuya cluster but need reversing
    {"_TZ3000_egq7y6pr", "TS130F", "Lonsonho", "11830304 Switch"},
    {"_TZ3000_xzqbrqk1", "TS130F", "Lonsonho", "Zigbee curtain switch"}, // https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757#issuecomment-776201454
    {"_TZ3000_ltiqubue", "TS130F", "Tuya", "Zigbee curtain switch"},
    {"_TZ3000_vd43bbfq", "TS130F", "Tuya", "QS-Zigbee-C01 Module"}, // Curtain module QS-Zigbee-C01

    // Other
    {"_TYST11_d0yu2xgi", "0yu2xgi", "NEO/Tuya", "NAS-AB02B0 Siren"},
    {"_TZE200_d0yu2xgi", "TS0601", "NEO/Tuya", "NAS-AB02B0 Siren"},
    {"_TZ3000_m0vaazab", "TS0207", "Tuya", "Tuya_RPT Repeater"},

    // Sensor
    {"_TZ3210_rxqls8v0", "TS0202", "Fantem", "Tuya_SEN Multi-sensor"},
    {"_TZ3210_zmy9hjay", "TS0202", "Fantem", "Tuya_SEN Multi-sensor"},

     // Switch
    {"_TZE200_dfxkcots", "TS0601", "Earda", "Tuya_DIMSWITCH Earda Dimmer"},
    {"_TZE200_9i9dt8is", "TS0601", "Earda", "Tuya_DIMSWITCH EDM-1ZAA-EU"},

    {nullptr, nullptr, nullptr, nullptr}
};

/*! Returns the product identifier for a matching Basic Cluster manufacturer name. */
static QLatin1String productIdForManufacturerName(const QString &manufacturerName, const ProductMap *mapIter)
{
    Q_ASSERT(mapIter);

    for (; mapIter->commonProductId != nullptr; mapIter++)
    {
        if (manufacturerName == QLatin1String(mapIter->zmanufacturerName))
        {
            return QLatin1String(mapIter->commonProductId);
        }
    }

    return QLatin1String("");
}

/*! Returns the product identifier for a resource. */
const QString R_GetProductId(Resource *resource)
{
    DBG_Assert(resource);

    if (!resource)
    {
        return { };
    }

    auto *productId = resource->item(RAttrProductId);

    if (productId)
    {
        return productId->toString();
    }

    const auto *manufacturerName = resource->item(RAttrManufacturerName);
    const auto *modelId = resource->item(RAttrModelId);

    // Need manufacturerName
    if (!manufacturerName)
    {
        return { };
    }

    //Tuya don't need modelId
    if (isTuyaManufacturerName(manufacturerName->toString()))
    {
        // for Tuya devices match against manufacturer name
        const auto productIdStr = productIdForManufacturerName(manufacturerName->toString(), products);
        if (productIdStr.size() > 0)
        {
            productId = resource->addItem(DataTypeString, RAttrProductId);
            DBG_Assert(productId);
            productId->setValue(QString(productIdStr));
            productId->setIsPublic(false); // not ready for public
            return productId->toString();
        }
        else
        {
            // Fallback
            // manufacturer name is the most unique identifier for Tuya
            if (DBG_IsEnabled(DBG_INFO_L2))
            {
                DBG_Printf(DBG_INFO_L2, "No Tuya productId entry found for manufacturername: %s\n", qPrintable(manufacturerName->toString()));
            }

            return manufacturerName->toString();
        }
    }

    if (modelId)
    {
        return modelId->toString();
    }

    return { };
}

/*! Returns true if the \p manufacturer name referes to a Tuya device. */
bool isTuyaManufacturerName(const QString &manufacturer)
{
    return manufacturer.startsWith(QLatin1String("_T")) && // quick check for performance
           std::regex_match(qPrintable(manufacturer), std::regex("_T[A-Z][A-Z0-9]{4}_[a-z0-9]{8}"));
}

// Tests for Tuya manufacturer name
/*
 Q_ASSERT(isTuyaManufacturerName("_TZ3000_bi6lpsew"));
 Q_ASSERT(isTuyaManufacturerName("_TYZB02_key8kk7r"));
 Q_ASSERT(isTuyaManufacturerName("_TYST11_ckud7u2l"));
 Q_ASSERT(isTuyaManufacturerName("_TYZB02_keyjqthh"));
 Q_ASSERT(!isTuyaManufacturerName("lumi.sensor_switch.aq2"));
*/

static const lidlDevice lidlDevices[] = { // Sorted by zigbeeManufacturerName
    { "_TYZB01_bngwdjsr", "TS1001",  "LIDL Livarno Lux", "HG06323" }, // Remote Control
    { "_TZ1800_ejwkn2h2", "TY0203",  "LIDL Silvercrest", "HG06336" }, // Contact sensor
    { "_TZ1800_fcdjzz3s", "TY0202",  "LIDL Silvercrest", "HG06335" }, // Motion sensor
    { "_TZ1800_ladpngdx", "TS0211",  "LIDL Silvercrest", "HG06668" }, // Door bell
    { "_TZ3000_1obwwnmq", "TS011F",  "LIDL Silvercrest", "HG06338" }, // Smart USB Extension Lead (EU)
    { "_TZ3000_49qchf10", "TS0502A", "LIDL Livarno Lux", "HG06492C" }, // CT Light (E27)
    { "_TZ3000_9cpuaca6", "TS0505A", "LIDL Livarno Lux", "14148906L" }, // Stimmungsleuchte
    { "_TZ3000_dbou1ap4", "TS0505A", "LIDL Livarno Lux", "HG06106C" }, // RGB Light (E27)
    { "_TZ3000_el5kt5im", "TS0502A", "LIDL Livarno Lux", "HG06492A" }, // CT Light (GU10)
    { "_TZ3000_gek6snaj", "TS0505A", "LIDL Livarno Lux", "14149506L" }, // Lichtleiste
    { "_TZ3000_kdi2o9m6", "TS011F",  "LIDL Silvercrest", "HG06337" }, // Smart plug (EU)
    { "_TZ3000_kdpxju99", "TS0505A", "LIDL Livarno Lux", "HG06106A" }, // RGB Light (GU10)
    { "_TZ3000_oborybow", "TS0502A", "LIDL Livarno Lux", "HG06492B" }, // CT Light (E14)
    { "_TZ3000_odygigth", "TS0505A", "LIDL Livarno Lux", "HG06106B" }, // RGB Light (E14)
    { "_TZ3000_riwp3k79", "TS0505A", "LIDL Livarno Lux", "HG06104A" }, // LED Light Strip
    { "_TZE200_s8gkrkxk", "TS0601",  "LIDL Livarno Lux", "HG06467" }, // Smart LED String Lights (EU)
    { nullptr, nullptr, nullptr, nullptr }
};

const lidlDevice *getLidlDevice(const QString &zigbeeManufacturerName)
{
    const lidlDevice *device = lidlDevices;

    while (device->zigbeeManufacturerName != nullptr)
    {
        if (zigbeeManufacturerName == QLatin1String(device->zigbeeManufacturerName))
        {
            return device;
        }
        device++;
    }
    return nullptr;
}

bool isLidlDevice(const QString &zigbeeModelIdentifier, const QString &manufacturername)
{
    const lidlDevice *device = lidlDevices;

    while (device->zigbeeManufacturerName != nullptr)
    {
        if (zigbeeModelIdentifier == QLatin1String(device->zigbeeModelIdentifier) &&
            manufacturername == QLatin1String(device->manufacturername))
        {
            return true;
        }
        device++;
    }
    return false;
}

uint productHash(const Resource *r)
{
    if (!r || !r->item(RAttrManufacturerName) || !r->item(RAttrModelId))
    {
        return 0;
    }

    if (isTuyaManufacturerName(r->item(RAttrManufacturerName)->toString()))
    {
        // for Tuya devices use manufacturer name as modelid
        return qHash(r->item(RAttrManufacturerName)->toString());
    }
    else
    {
        return qHash(r->item(RAttrModelId)->toString());
    }
}
<commit_msg>Update product_match.cpp<commit_after>/*
 * Copyright (c) 2021 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <regex>
#include <deconz/dbg_trace.h>

#include "product_match.h"
#include "resource.h"

/*! The product map is a helper to map Basic Cluster manufacturer name and modelid
   to human readable product identifiers like marketing string or the model no. as printed on the product package.

   In case of Tuya multiple entries may refer to the same device, so in matching code
   it's best to match against the \c productId.

   Example:

   if (R_GetProductId(sensor) == QLatin1String("SEA801-ZIGBEE TRV"))
   {
   }

   Note: this will later on be replaced with the data from DDF files.
*/
struct ProductMap
{
    const char *zmanufacturerName;
    const char *zmodelId;
    const char *manufacturer;
    // a common product identifier even if multipe branded versions exist
    const char *commonProductId;
};

static const ProductMap products[] =
{
    // Prefix signification
    // --------------------
    // Tuya_THD : thermostat device using Tuya cluster
    // Tuya_COVD : covering device using Tuya cluster
    // Tuya_RPT : Repeater
    // Tuya_SEN : Sensor

    // Tuya Thermostat / TRV
    {"_TYST11_zuhszj9s", "uhszj9s", "HiHome", "Tuya_THD WZB-TRVL TRV"},
    {"_TYST11_KGbxAXL2", "GbxAXL2", "Saswell", "Tuya_THD SEA801-ZIGBEE TRV"},
    {"_TYST11_c88teujp", "88teujp", "Saswell", "Tuya_THD SEA801-ZIGBEE TRV"},
    {"_TZE200_c88teujp", "TS0601", "Saswell", "Tuya_THD SEA801-ZIGBEE TRV"},
    {"_TYST11_ckud7u2l", "kud7u2l", "Tuya", "Tuya_THD HY369 TRV"},
    {"_TZE200_ckud7u2l", "TS0601", "Tuya", "Tuya_THD HY369 TRV"},
    {"_TZE200_ywdxldoj", "TS0601", "MOES/tuya", "Tuya_THD HY368 TRV"},
    {"_TZE200_fhn3negr", "TS0601", "MOES/tuya", "Tuya_THD MOES TRV"},
    {"_TZE200_aoclfnxz", "TS0601", "Moes", "Tuya_THD BTH-002 Thermostat"},
    {"_TYST11_jeaxp72v", "eaxp72v", "Essentials", "Tuya_THD Essentials TRV"},
    {"_TYST11_kfvq6avy", "fvq6avy", "Revolt", "Tuya_THD NX-4911-675 TRV"},
    {"_TZE200_kfvq6avy", "TS0601", "Revolt", "Tuya_THD NX-4911-675 TRV"},
    {"_TYST11_zivfvd7h", "ivfvd7h", "Siterwell", "Tuya_THD GS361A-H04 TRV"},
    {"_TZE200_zivfvd7h", "TS0601", "Siterwell", "Tuya_THD GS361A-H04 TRV"},
    {"_TYST11_yw7cahqs", "w7cahqs", "Hama", "Tuya_THD Smart radiator TRV"},
    {"_TZE200_yw7cahqs", "TS0601", "Hama", "Tuya_THD Smart radiator TRV"},
    {"_TZE200_cwnjrr72", "TS0601", "MOES", "Tuya_THD HY368 TRV"},

    // Tuya Covering
    {"_TYST11_wmcdj3aq", "mcdj3aq", "Zemismart", "Tuya_COVD ZM25TQ"},
    {"_TZE200_wmcdj3aq", "TS0601", "Zemismart", "Tuya_COVD ZM25TQ"},
    {"_TZE200_fzo2pocs", "TS0601", "Zemismart", "Tuya_COVD ZM25TQ"},
    {"_TYST11_xu1rkty3", "u1rkty3", "Smart Home", "Tuya_COVD DT82LEMA-1.2N"},
    {"_TZE200_xuzcvlku", "TS0601", "Zemismart", "Tuya_COVD M515EGB"},
    {"_TZE200_rddyvrci", "TS0601", "Moes", "Tuya_COVD AM43-0.45/40-ES-EZ(TY)"},
    {"_TZE200_zah67ekd", "TS0601", "MoesHouse / Livolo", "Tuya_COVD AM43-0.45-40"},
    {"_TZE200_nogaemzt", "TS0601", "Tuya", "Tuya_COVD YS-MT750"},
    {"_TZE200_zpzndjez", "TS0601", "Tuya", "Tuya_COVD DS82"},
    {"_TZE200_cowvfni3", "TS0601", "Zemismart", "Tuya_COVD ZM79E-DT"},
    {"_TZE200_5zbp6j0u", "TS0601", "Tuya/Zemismart", "Tuya_COVD DT82LEMA-1.2N"},
    {"_TZE200_fdtjuw7u", "TS0601", "Yushun", "Tuya_COVD YS-MT750"},
    {"_TZE200_bqcqqjpb", "TS0601", "Yushun", "Tuya_COVD YS-MT750"},
    {"_TZE200_nueqqe6k", "TS0601", "Zemismart", "Tuya_COVD M515EGB"},

    // Tuya covering not using tuya cluster but need reversing
    {"_TZ3000_egq7y6pr", "TS130F", "Lonsonho", "11830304 Switch"},
    {"_TZ3000_xzqbrqk1", "TS130F", "Lonsonho", "Zigbee curtain switch"}, // https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757#issuecomment-776201454
    {"_TZ3000_ltiqubue", "TS130F", "Tuya", "Zigbee curtain switch"},
    {"_TZ3000_vd43bbfq", "TS130F", "Tuya", "QS-Zigbee-C01 Module"}, // Curtain module QS-Zigbee-C01

    // Other
    {"_TYST11_d0yu2xgi", "0yu2xgi", "NEO/Tuya", "NAS-AB02B0 Siren"},
    {"_TZE200_d0yu2xgi", "TS0601", "NEO/Tuya", "NAS-AB02B0 Siren"},
    {"_TZ3000_m0vaazab", "TS0207", "Tuya", "Tuya_RPT Repeater"},

    // Sensor
    {"_TZ3210_rxqls8v0", "TS0202", "Fantem", "Tuya_SEN Multi-sensor"},
    {"_TZ3210_zmy9hjay", "TS0202", "Fantem", "Tuya_SEN Multi-sensor"},

     // Switch
    {"_TZE200_la2c2uo9", "TS0601", "Moes", "Tuya_DIMSWITCH MS-105Z"},
    {"_TZE200_dfxkcots", "TS0601", "Earda", "Tuya_DIMSWITCH Earda Dimmer"},
    {"_TZE200_9i9dt8is", "TS0601", "Earda", "Tuya_DIMSWITCH EDM-1ZAA-EU"},

    {nullptr, nullptr, nullptr, nullptr}
};

/*! Returns the product identifier for a matching Basic Cluster manufacturer name. */
static QLatin1String productIdForManufacturerName(const QString &manufacturerName, const ProductMap *mapIter)
{
    Q_ASSERT(mapIter);

    for (; mapIter->commonProductId != nullptr; mapIter++)
    {
        if (manufacturerName == QLatin1String(mapIter->zmanufacturerName))
        {
            return QLatin1String(mapIter->commonProductId);
        }
    }

    return QLatin1String("");
}

/*! Returns the product identifier for a resource. */
const QString R_GetProductId(Resource *resource)
{
    DBG_Assert(resource);

    if (!resource)
    {
        return { };
    }

    auto *productId = resource->item(RAttrProductId);

    if (productId)
    {
        return productId->toString();
    }

    const auto *manufacturerName = resource->item(RAttrManufacturerName);
    const auto *modelId = resource->item(RAttrModelId);

    // Need manufacturerName
    if (!manufacturerName)
    {
        return { };
    }

    //Tuya don't need modelId
    if (isTuyaManufacturerName(manufacturerName->toString()))
    {
        // for Tuya devices match against manufacturer name
        const auto productIdStr = productIdForManufacturerName(manufacturerName->toString(), products);
        if (productIdStr.size() > 0)
        {
            productId = resource->addItem(DataTypeString, RAttrProductId);
            DBG_Assert(productId);
            productId->setValue(QString(productIdStr));
            productId->setIsPublic(false); // not ready for public
            return productId->toString();
        }
        else
        {
            // Fallback
            // manufacturer name is the most unique identifier for Tuya
            if (DBG_IsEnabled(DBG_INFO_L2))
            {
                DBG_Printf(DBG_INFO_L2, "No Tuya productId entry found for manufacturername: %s\n", qPrintable(manufacturerName->toString()));
            }

            return manufacturerName->toString();
        }
    }

    if (modelId)
    {
        return modelId->toString();
    }

    return { };
}

/*! Returns true if the \p manufacturer name referes to a Tuya device. */
bool isTuyaManufacturerName(const QString &manufacturer)
{
    return manufacturer.startsWith(QLatin1String("_T")) && // quick check for performance
           std::regex_match(qPrintable(manufacturer), std::regex("_T[A-Z][A-Z0-9]{4}_[a-z0-9]{8}"));
}

// Tests for Tuya manufacturer name
/*
 Q_ASSERT(isTuyaManufacturerName("_TZ3000_bi6lpsew"));
 Q_ASSERT(isTuyaManufacturerName("_TYZB02_key8kk7r"));
 Q_ASSERT(isTuyaManufacturerName("_TYST11_ckud7u2l"));
 Q_ASSERT(isTuyaManufacturerName("_TYZB02_keyjqthh"));
 Q_ASSERT(!isTuyaManufacturerName("lumi.sensor_switch.aq2"));
*/

static const lidlDevice lidlDevices[] = { // Sorted by zigbeeManufacturerName
    { "_TYZB01_bngwdjsr", "TS1001",  "LIDL Livarno Lux", "HG06323" }, // Remote Control
    { "_TZ1800_ejwkn2h2", "TY0203",  "LIDL Silvercrest", "HG06336" }, // Contact sensor
    { "_TZ1800_fcdjzz3s", "TY0202",  "LIDL Silvercrest", "HG06335" }, // Motion sensor
    { "_TZ1800_ladpngdx", "TS0211",  "LIDL Silvercrest", "HG06668" }, // Door bell
    { "_TZ3000_1obwwnmq", "TS011F",  "LIDL Silvercrest", "HG06338" }, // Smart USB Extension Lead (EU)
    { "_TZ3000_49qchf10", "TS0502A", "LIDL Livarno Lux", "HG06492C" }, // CT Light (E27)
    { "_TZ3000_9cpuaca6", "TS0505A", "LIDL Livarno Lux", "14148906L" }, // Stimmungsleuchte
    { "_TZ3000_dbou1ap4", "TS0505A", "LIDL Livarno Lux", "HG06106C" }, // RGB Light (E27)
    { "_TZ3000_el5kt5im", "TS0502A", "LIDL Livarno Lux", "HG06492A" }, // CT Light (GU10)
    { "_TZ3000_gek6snaj", "TS0505A", "LIDL Livarno Lux", "14149506L" }, // Lichtleiste
    { "_TZ3000_kdi2o9m6", "TS011F",  "LIDL Silvercrest", "HG06337" }, // Smart plug (EU)
    { "_TZ3000_kdpxju99", "TS0505A", "LIDL Livarno Lux", "HG06106A" }, // RGB Light (GU10)
    { "_TZ3000_oborybow", "TS0502A", "LIDL Livarno Lux", "HG06492B" }, // CT Light (E14)
    { "_TZ3000_odygigth", "TS0505A", "LIDL Livarno Lux", "HG06106B" }, // RGB Light (E14)
    { "_TZ3000_riwp3k79", "TS0505A", "LIDL Livarno Lux", "HG06104A" }, // LED Light Strip
    { "_TZE200_s8gkrkxk", "TS0601",  "LIDL Livarno Lux", "HG06467" }, // Smart LED String Lights (EU)
    { nullptr, nullptr, nullptr, nullptr }
};

const lidlDevice *getLidlDevice(const QString &zigbeeManufacturerName)
{
    const lidlDevice *device = lidlDevices;

    while (device->zigbeeManufacturerName != nullptr)
    {
        if (zigbeeManufacturerName == QLatin1String(device->zigbeeManufacturerName))
        {
            return device;
        }
        device++;
    }
    return nullptr;
}

bool isLidlDevice(const QString &zigbeeModelIdentifier, const QString &manufacturername)
{
    const lidlDevice *device = lidlDevices;

    while (device->zigbeeManufacturerName != nullptr)
    {
        if (zigbeeModelIdentifier == QLatin1String(device->zigbeeModelIdentifier) &&
            manufacturername == QLatin1String(device->manufacturername))
        {
            return true;
        }
        device++;
    }
    return false;
}

uint productHash(const Resource *r)
{
    if (!r || !r->item(RAttrManufacturerName) || !r->item(RAttrModelId))
    {
        return 0;
    }

    if (isTuyaManufacturerName(r->item(RAttrManufacturerName)->toString()))
    {
        // for Tuya devices use manufacturer name as modelid
        return qHash(r->item(RAttrManufacturerName)->toString());
    }
    else
    {
        return qHash(r->item(RAttrModelId)->toString());
    }
}
<|endoftext|>


<commit_before>#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_system.h"
#include "esp_task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "driver/gpio.h"

#include "led_strip/led_strip.h"

#include "animations/display.h"
#include "animations/SmearAnimation.h"
#include "animations/SpiralAnimation.h"
#include "animations/MeteorShowerAnimation.h"
#include "animations/Felix.h"
#include "animations/AnimationProgram.h"
#include "animations/HeartAnimation.h"
#include "animations/JsonAnimation.h"
#include "animations/SpinBottleAnimation.h"
#include "animations/WormholeAnimation.h"
#include "animations/Timer.h"

#include <stdio.h>

#include "blewiz/BLECore.h"

#include "BadgeService.h"

extern "C" {
	void app_main(void);
}

static const char* TAG = "BADGE";

#define LED_STRIP_LENGTH 64U
#define LED_STRIP_RMT_INTR_NUM 19U

static Display display( GPIO_NUM_14);

static SpiralAnimation spiralAnimation(display);
static MeteorShowerAnimation meteorShowerAnimation(display);
static SmearAnimation smearAnimation(display);
static Felix felix(display);
static HeartAnimation heart1Animation(display);
static JsonAnimation testJson(display);
static SpinBottleAnimation spinBottle(display);
static WormholeAnimation wormhole(display);

Timer animator; // todo parameterize
AnimationProgram animationProgram(animator);

/* static */
BadgeService badgeService(display, animationProgram);

void app_main(void) {
    // fixme check error
    nvs_flash_init();

    ESP_LOGI(TAG, "initializing");

    // fixme check duplicate index error
    animationProgram.putAnimation(0, &meteorShowerAnimation);
    animationProgram.putAnimation(1, &spiralAnimation);
    animationProgram.putAnimation(2, &smearAnimation);
    animationProgram.putAnimation(3, &felix);
    animationProgram.putAnimation(4, &heart1Animation);
    animationProgram.putAnimation(5, &spinBottle);
    animationProgram.putAnimation(6, &testJson);
    animationProgram.putAnimation(7, &wormhole);

    testJson.loadJson();

    badgeService.init();

    BLECore core;
    core.registerService(&badgeService);
    core.init();

    animator.setCallback(
        [] {
            animationProgram.drawFrame();
        }
    );

    display.setBrightness(5);

    animator.start();
}

<commit_msg>New order for programs<commit_after>#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_system.h"
#include "esp_task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "driver/gpio.h"

#include "led_strip/led_strip.h"

#include "animations/display.h"
#include "animations/SmearAnimation.h"
#include "animations/SpiralAnimation.h"
#include "animations/MeteorShowerAnimation.h"
#include "animations/Felix.h"
#include "animations/AnimationProgram.h"
#include "animations/HeartAnimation.h"
#include "animations/JsonAnimation.h"
#include "animations/SpinBottleAnimation.h"
#include "animations/WormholeAnimation.h"
#include "animations/Timer.h"

#include <stdio.h>

#include "blewiz/BLECore.h"

#include "BadgeService.h"

extern "C" {
	void app_main(void);
}

static const char* TAG = "BADGE";

#define LED_STRIP_LENGTH 64U
#define LED_STRIP_RMT_INTR_NUM 19U

static Display display( GPIO_NUM_14);

static SpiralAnimation spiralAnimation(display);
static MeteorShowerAnimation meteorShowerAnimation(display);
static SmearAnimation smearAnimation(display);
static Felix felix(display);
static HeartAnimation heart1Animation(display);
static JsonAnimation testJson(display);
static SpinBottleAnimation spinBottle(display);
static WormholeAnimation wormhole(display);

Timer animator; // todo parameterize
AnimationProgram animationProgram(animator);

/* static */
BadgeService badgeService(display, animationProgram);

void app_main(void) {
    // fixme check error
    nvs_flash_init();

    ESP_LOGI(TAG, "initializing");

    // fixme check duplicate index error
    animationProgram.putAnimation(0, &meteorShowerAnimation);
    animationProgram.putAnimation(1, &spiralAnimation);
    animationProgram.putAnimation(2, &smearAnimation);
    animationProgram.putAnimation(3, &felix);
    animationProgram.putAnimation(4, &heart1Animation);
    animationProgram.putAnimation(5, &spinBottle);
    animationProgram.putAnimation(6, &wormhole);
    animationProgram.putAnimation(7, &testJson);

    testJson.loadJson();

    badgeService.init();

    BLECore core;
    core.registerService(&badgeService);
    core.init();

    animator.setCallback(
        [] {
            animationProgram.drawFrame();
        }
    );

    display.setBrightness(5);

    animator.start();
}

<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <map>
#include <node.h>

#include "PSATResult.h"
#include "calculator/EstimateFLA.h"

using namespace v8;
using namespace std;

Isolate* iso;
Local<Object> inp;

double Get(const char *nm) {
  auto rObj = inp->ToObject()->Get(String::NewFromUtf8(iso,nm));
  if (rObj->IsUndefined()) {
    cout << nm << endl;;
    assert(!"defined");
  }
  return rObj->NumberValue();
}

void Results(const FunctionCallbackInfo<Value>& args) {
  iso = args.GetIsolate();
  inp = args[0]->ToObject();
  auto r = Object::New(iso);

  auto drive = (Pump::Drive)(int)Get("drive");
  auto effCls = (Motor::EfficiencyClass)(int)Get("efficiency_class");
  Pump pump((Pump::Style)(int)Get("pump_style"),Get("pump_specified"),Get("pump_rated_speed"),drive,
      Get("viscosity"),Get("specific_gravity"),Get("stages"),(Pump::Speed)(int)(!Get("fixed_speed")));
  Motor motor((Motor::LineFrequency)(int)(!Get("line")),Get("motor_rated_power"),Get("motor_rated_speed"),
      effCls,Get("efficiency"),Get("motor_rated_voltage"),Get("motor_rated_flc"),Get("margin"));
  Financial fin(Get("fraction"),Get("cost"));
  FieldData fd(Get("flow"),Get("head"),(FieldData::LoadEstimationMethod)(Get("motor_field_power")>0?0:1),
      Get("motor_field_power"),Get("motor_field_current"),Get("motor_field_voltage"));
  PSATResult psat(pump,motor,fin,fd);
  psat.calculateExisting();
  psat.calculateOptimal();
  auto ex = psat.getExisting(), opt = psat.getOptimal();

  map<const char *,vector<double>> out = {
    {"Pump Efficiency",{ex.pumpEfficiency_*100,opt.pumpEfficiency_*100}},
    {"Motor Rated Power",{ex.motorRatedPower_,opt.motorRatedPower_}},
    {"Motor Shaft Power",{ex.motorShaftPower_,opt.motorShaftPower_}},
    {"Pump Shaft Power",{ex.pumpShaftPower_,opt.pumpShaftPower_}},
    {"Motor Efficiency",{ex.motorEfficiency_,opt.motorEfficiency_}},
    {"Motor Power Factor",{ex.motorPowerFactor_,opt.motorPowerFactor_}},
    {"Motor Current",{ex.motorCurrent_,opt.motorCurrent_}},
    {"Motor Power", {ex.motorPower_,opt.motorPower_}},
    {"Annual Energy", {ex.annualEnergy_,opt.annualEnergy_}},
    {"Annual Cost", {ex.annualCost_*1000,opt.annualCost_*1000}},
    {"Savings Potential", {psat.getAnnualSavingsPotential(),-1}},
    {"Optimization Rating", {psat.getOptimizationRating(),-1}}
  };
  for(auto p: out) {
    auto a = Array::New(iso);
    a->Set(0,Number::New(iso,p.second[0]));
    a->Set(1,Number::New(iso,p.second[1]));
    r->Set(String::NewFromUtf8(iso,p.first),a);
  }
  args.GetReturnValue().Set(r);
}

void EstFLA(const FunctionCallbackInfo<Value>& args) {
  iso = args.GetIsolate();
  inp = args[0]->ToObject();
  EstimateFLA fla(Get("motor_rated_power"),Get("motor_rated_speed"),(Motor::LineFrequency)(int)(!Get("line")),(Motor::EfficiencyClass)(int)Get("efficiency_class"),
    Get("efficiency"),Get("motor_rated_voltage"));
  fla.calculate();
  args.GetReturnValue().Set(fla.getEstimatedFLA());
}

//TODO round vs js round; loosen up to make next test case
void Check(double exp, double act, const char* nm="") {
  //cout << "e " << exp << "; a " << act << endl;
  // if (isnan(act) || (abs(exp-act)>.01*exp)) {
  auto p = 10;
  if (isnan(act) || ( (round(exp*p)/p)!=round(act*p)/p)) {
    printf("\"%s\" TEST FAILED: %f %f\n",nm,exp,act);
    assert(!"equal");
  }
}

void Check100(double exp, double act, const char* nm="") {
  Check(exp,act*100,nm);
}

void Test(const FunctionCallbackInfo<Value>& args) {
  EstimateFLA fla(200,1780,(Motor::LineFrequency)1,(Motor::EfficiencyClass)(1),0,460);
  fla.calculate();
  Check(225.8,fla.getEstimatedFLA());

  #define BASE \
    Pump pump(Pump::Style::END_SUCTION_ANSI_API,0,1780,Pump::Drive::DIRECT_DRIVE,\
      1,1,1,Pump::Speed::NOT_FIXED_SPEED);\
    Motor motor(Motor::LineFrequency::FREQ60,200,1780,\
        Motor::EfficiencyClass::ENERGY_EFFICIENT,0,460,225.8,0);\
    Financial fin(1,.05);\
    FieldData fd(2000,277,FieldData::LoadEstimationMethod::POWER,\
        150,0,460);

  #define CALC \
    PSATResult psat(pump,motor,fin,fd);\
    psat.calculateExisting();\
    auto ex = psat.getExisting();

  for (int i=1; i<=10000; i=i+2) {
    BASE
    CALC
    Check(ex.motorShaftPower_,ex.motorShaftPower_,"SAME");
  }

  {
    BASE
    motor.setMotorRpm(1786);
    fd.setMotorPower(80);
    CALC
    Check(101.9,ex.motorShaftPower_);
    Check100(95,ex.motorEfficiency_);
    Check100(79.1,ex.motorPowerFactor_);
    Check(127,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(80);
    motor.setMotorRatedPower(100);
    motor.setFullLoadAmps(113.8);
    CALC
    Check(101.8,ex.motorShaftPower_);
    Check100(94.9,ex.motorEfficiency_);
    Check100(86.7,ex.motorPowerFactor_);
    Check(115.8,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(80);
    fd.setVoltage(260);
    CALC
    Check(101.9,ex.motorShaftPower_);
    Check100(95,ex.motorEfficiency_);
    Check100(138.8,ex.motorPowerFactor_);
    Check(128,ex.motorCurrent_);
  }
  {
    BASE
    motor.setMotorRpm(1200);
    fd.setMotorPower(80);
    motor.setFullLoadAmps(235.3);
    CALC
    Check(101.4,ex.motorShaftPower_);
    Check100(94.5,ex.motorEfficiency_);
    Check100(74.3,ex.motorPowerFactor_);
    Check(135.1,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(111.855);
    CALC
    Check(143.4,ex.motorShaftPower_);
    Check100(95.6,ex.motorEfficiency_);
    Check100(84.3,ex.motorPowerFactor_);
    Check(166.5,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(80);
    motor.setMotorRatedVoltage(200);
    motor.setFullLoadAmps(519.3);
    CALC
    Check(101.9,ex.motorShaftPower_);
    Check100(95,ex.motorEfficiency_);
    Check100(35.2,ex.motorPowerFactor_);
    Check(284.9,ex.motorCurrent_);
  }
  {
    BASE
    CALC
    Check(217.5,ex.motorCurrent_);
  }
  {
    BASE
    fd.setLoadEstimationMethod(FieldData::LoadEstimationMethod::CURRENT);
    fd.setMotorAmps(218);
    fd.setMotorPower(0);
    CALC
    Check(150.4,ex.motorPower_);
    Check100(72.5,ex.pumpEfficiency_);
  }
  {
    BASE
    fd.setMotorPower(80);
    CALC
    Check(700.8,ex.annualEnergy_);
  }
  {
    BASE
    fin.setOperatingFraction(.25);
    CALC
    Check(328.5,ex.annualEnergy_);
    Check(16.4,ex.annualCost_);
  }
  {
    BASE
    motor.setFullLoadAmps(300);
    CALC
    Check(288.9,ex.motorCurrent_);
  }
  {
    BASE
    motor.setEfficiencyClass(Motor::EfficiencyClass(0));
    CALC
    Check(213.7,ex.motorCurrent_);
  }
  {
    BASE
    motor.setEfficiencyClass(Motor::EfficiencyClass(2));
    motor.setSpecifiedEfficiency(75);
    CALC
    Check(173.7,ex.motorCurrent_);
  }
  cout << "done";
}

void Wtf(const FunctionCallbackInfo<Value>& args) {
}

void Init(Local<Object> exports) {
  NODE_SET_METHOD(exports, "results", Results);
  NODE_SET_METHOD(exports, "estFLA", EstFLA);
  NODE_SET_METHOD(exports, "test", Test);
  NODE_SET_METHOD(exports, "wtf", Wtf);
}

NODE_MODULE(bridge, Init)

<commit_msg>no message<commit_after>#include <iostream>
#include <vector>
#include <map>
#include <node.h>

#include "PSATResult.h"
#include "calculator/EstimateFLA.h"

using namespace v8;
using namespace std;

Isolate* iso;
Local<Object> inp;

double Get(const char *nm) {
  auto rObj = inp->ToObject()->Get(String::NewFromUtf8(iso,nm));
  if (rObj->IsUndefined()) {
    cout << nm << endl;;
    assert(!"defined");
  }
  return rObj->NumberValue();
}

void Results(const FunctionCallbackInfo<Value>& args) {
  iso = args.GetIsolate();
  inp = args[0]->ToObject();
  auto r = Object::New(iso);

  auto drive = (Pump::Drive)(int)Get("drive");
  auto effCls = (Motor::EfficiencyClass)(int)Get("efficiency_class");
  Pump pump((Pump::Style)(int)Get("pump_style"),Get("pump_specified"),Get("pump_rated_speed"),drive,
      Get("viscosity"),Get("specific_gravity"),Get("stages"),(Pump::Speed)(int)(!Get("fixed_speed")));
  Motor motor((Motor::LineFrequency)(int)(!Get("line")),Get("motor_rated_power"),Get("motor_rated_speed"),
      effCls,Get("efficiency"),Get("motor_rated_voltage"),Get("motor_rated_flc"),Get("margin"));
  Financial fin(Get("fraction"),Get("cost"));
  FieldData fd(Get("flow"),Get("head"),(FieldData::LoadEstimationMethod)(Get("motor_field_power")>0?0:1),
      Get("motor_field_power"),Get("motor_field_current"),Get("motor_field_voltage"));
  PSATResult psat(pump,motor,fin,fd);
  psat.calculateExisting();
  psat.calculateOptimal();
  auto ex = psat.getExisting(), opt = psat.getOptimal();

  map<const char *,vector<double>> out = {
    {"Pump Efficiency",{ex.pumpEfficiency_*100,opt.pumpEfficiency_*100}},
    {"Motor Rated Power",{ex.motorRatedPower_,opt.motorRatedPower_}},
    {"Motor Shaft Power",{ex.motorShaftPower_,opt.motorShaftPower_}},
    {"Pump Shaft Power",{ex.pumpShaftPower_,opt.pumpShaftPower_}},
    {"Motor Efficiency",{ex.motorEfficiency_,opt.motorEfficiency_}},
    {"Motor Power Factor",{ex.motorPowerFactor_,opt.motorPowerFactor_}},
    {"Motor Current",{ex.motorCurrent_,opt.motorCurrent_}},
    {"Motor Power", {ex.motorPower_,opt.motorPower_}},
    {"Annual Energy", {ex.annualEnergy_,opt.annualEnergy_}},
    {"Annual Cost", {ex.annualCost_*1000,opt.annualCost_*1000}},
    {"Savings Potential", {psat.getAnnualSavingsPotential(),-1}},
    {"Optimization Rating", {psat.getOptimizationRating(),-1}}
  };
  for(auto p: out) {
    auto a = Array::New(iso);
    a->Set(0,Number::New(iso,p.second[0]));
    a->Set(1,Number::New(iso,p.second[1]));
    r->Set(String::NewFromUtf8(iso,p.first),a);
  }
  args.GetReturnValue().Set(r);
}

void EstFLA(const FunctionCallbackInfo<Value>& args) {
  iso = args.GetIsolate();
  inp = args[0]->ToObject();
  EstimateFLA fla(Get("motor_rated_power"),Get("motor_rated_speed"),(Motor::LineFrequency)(int)(!Get("line")),(Motor::EfficiencyClass)(int)Get("efficiency_class"),
    Get("efficiency"),Get("motor_rated_voltage"));
  fla.calculate();
  args.GetReturnValue().Set(fla.getEstimatedFLA());
}

//TODO round vs js round; loosen up to make next test case
void Check(double exp, double act, const char* nm="") {
  //cout << "e " << exp << "; a " << act << endl;
  // if (isnan(act) || (abs(exp-act)>.01*exp)) {
  auto p = 10;
  if (isnan(act) || ( (round(exp*p)/p)!=round(act*p)/p)) {
    printf("\"%s\" TEST FAILED: %f %f\n",nm,exp,act);
    assert(!"equal");
  }
}

void Check100(double exp, double act, const char* nm="") {
  Check(exp,act*100,nm);
}

void Test(const FunctionCallbackInfo<Value>& args) {
  MotorEfficiency mef(1780,Motor::EfficiencyClass::ENERGY_EFFICIENT,0,200,.75);
  Check100(95.69,mef.calculate());

  EstimateFLA fla(200,1780,(Motor::LineFrequency)1,(Motor::EfficiencyClass)(1),0,460);
  fla.calculate();
  Check(225.8,fla.getEstimatedFLA());

  #define BASE \
    Pump pump(Pump::Style::END_SUCTION_ANSI_API,0,1780,Pump::Drive::DIRECT_DRIVE,\
      1,1,1,Pump::Speed::NOT_FIXED_SPEED);\
    Motor motor(Motor::LineFrequency::FREQ60,200,1780,\
        Motor::EfficiencyClass::ENERGY_EFFICIENT,0,460,225.8,0);\
    Financial fin(1,.05);\
    FieldData fd(2000,277,FieldData::LoadEstimationMethod::POWER,\
        150,0,460);

  #define CALC \
    PSATResult psat(pump,motor,fin,fd);\
    psat.calculateExisting();\
    auto ex = psat.getExisting();

  for (int i=1; i<=10000; i=i+2) {
    BASE
    CALC
    Check(ex.motorShaftPower_,ex.motorShaftPower_,"SAME");
  }

  {
    BASE
    motor.setMotorRpm(1786);
    fd.setMotorPower(80);
    CALC
    Check(101.9,ex.motorShaftPower_);
    Check100(95,ex.motorEfficiency_);
    Check100(79.1,ex.motorPowerFactor_);
    Check(127,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(80);
    motor.setMotorRatedPower(100);
    motor.setFullLoadAmps(113.8);
    CALC
    Check(101.8,ex.motorShaftPower_);
    Check100(94.9,ex.motorEfficiency_);
    Check100(86.7,ex.motorPowerFactor_);
    Check(115.8,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(80);
    fd.setVoltage(260);
    CALC
    Check(101.9,ex.motorShaftPower_);
    Check100(95,ex.motorEfficiency_);
    Check100(138.8,ex.motorPowerFactor_);
    Check(128,ex.motorCurrent_);
  }
  {
    BASE
    motor.setMotorRpm(1200);
    fd.setMotorPower(80);
    motor.setFullLoadAmps(235.3);
    CALC
    Check(101.4,ex.motorShaftPower_);
    Check100(94.5,ex.motorEfficiency_);
    Check100(74.3,ex.motorPowerFactor_);
    Check(135.1,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(111.855);
    CALC
    Check(143.4,ex.motorShaftPower_);
    Check100(95.6,ex.motorEfficiency_);
    Check100(84.3,ex.motorPowerFactor_);
    Check(166.5,ex.motorCurrent_);
  }
  {
    BASE
    fd.setMotorPower(80);
    motor.setMotorRatedVoltage(200);
    motor.setFullLoadAmps(519.3);
    CALC
    Check(101.9,ex.motorShaftPower_);
    Check100(95,ex.motorEfficiency_);
    Check100(35.2,ex.motorPowerFactor_);
    Check(284.9,ex.motorCurrent_);
  }
  {
    BASE
    CALC
    Check(217.5,ex.motorCurrent_);
  }
  {
    BASE
    fd.setLoadEstimationMethod(FieldData::LoadEstimationMethod::CURRENT);
    fd.setMotorAmps(218);
    fd.setMotorPower(0);
    CALC
    Check(150.4,ex.motorPower_);
    Check100(72.5,ex.pumpEfficiency_);
  }
  {
    BASE
    fd.setMotorPower(80);
    CALC
    Check(700.8,ex.annualEnergy_);
  }
  {
    BASE
    fin.setOperatingFraction(.25);
    CALC
    Check(328.5,ex.annualEnergy_);
    Check(16.4,ex.annualCost_);
  }
  {
    BASE
    motor.setFullLoadAmps(300);
    CALC
    Check(288.9,ex.motorCurrent_);
  }
  {
    BASE
    motor.setEfficiencyClass(Motor::EfficiencyClass(0));
    CALC
    Check(213.7,ex.motorCurrent_);
  }
  {
    BASE
    motor.setEfficiencyClass(Motor::EfficiencyClass(2));
    motor.setSpecifiedEfficiency(75);
    CALC
    Check(173.7,ex.motorCurrent_);
  }
  cout << "done";
}

void Wtf(const FunctionCallbackInfo<Value>& args) {
}

void Init(Local<Object> exports) {
  NODE_SET_METHOD(exports, "results", Results);
  NODE_SET_METHOD(exports, "estFLA", EstFLA);
  NODE_SET_METHOD(exports, "test", Test);
  NODE_SET_METHOD(exports, "wtf", Wtf);
}

NODE_MODULE(bridge, Init)

<|endoftext|>


<commit_before>/*
 * Copyright 2012 Matthew McCormick
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * */
// This file was Authored by Justin Crawford <Justasic@gmail.com>
// Based on: github.com/freebsd/freebsd/blob/master/usr.bin/top/machine.c
// Based on: Apple.cpp for load_string/mem_string and apple's documentation

#include <sys/types.h>

#include "common.h"
#include "cpu.h"

float cpu_percentage(unsigned int cpu_usage_delay)
{
    int64_t load1[CPUSTATES];
    int64_t load2[CPUSTATES];

    GETSYSCTL("kern.cp_time", load1);
    usleep(cpu_usage_delay);
    GETSYSCTL("kern.cp_time", load2);

    // Current load times
    unsigned long long current_user = load1[CP_USER];
    unsigned long long current_system = load1[CP_SYS];
    unsigned long long current_nice = load1[CP_NICE];
    unsigned long long current_idle = load1[CP_IDLE];
    // Next load times
    unsigned long long next_user = load2[CP_USER];
    unsigned long long next_system = load2[CP_SYS];
    unsigned long long next_nice = load2[CP_NICE];
    unsigned long long next_idle = load2[CP_IDLE];
    // Difference between the two
    unsigned long long diff_user = next_user - current_user;
    unsigned long long diff_system = next_system - current_system;
    unsigned long long diff_nice = next_nice - current_nice;
    unsigned long long diff_idle = next_idle - current_idle;

    return static_cast<float>(diff_user + diff_system + diff_nice) /
	  static_cast<float>(diff_user + diff_system + diff_nice + diff_idle) *
	  100.0;
}
<commit_msg>fixed incorrect cpu usage<commit_after>/*
 * Copyright 2012 Matthew McCormick
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * */
// This file was Authored by Justin Crawford <Justasic@gmail.com>
// Based on: github.com/freebsd/freebsd/blob/master/usr.bin/top/machine.c
// Based on: Apple.cpp for load_string/mem_string and apple's documentation

#include <sys/types.h>
#include <unistd.h> // usleep

#include "common.h"
#include "cpu.h"

float cpu_percentage(unsigned int cpu_usage_delay)
{
    int32_t load1[CPUSTATES];
    int32_t load2[CPUSTATES];

    GETSYSCTL("kern.cp_time", load1);
    usleep(cpu_usage_delay);
    GETSYSCTL("kern.cp_time", load2);

    // Current load times
    unsigned long long current_user = load1[CP_USER];
    unsigned long long current_system = load1[CP_SYS];
    unsigned long long current_nice = load1[CP_NICE];
    unsigned long long current_idle = load1[CP_IDLE];
    // Next load times
    unsigned long long next_user = load2[CP_USER];
    unsigned long long next_system = load2[CP_SYS];
    unsigned long long next_nice = load2[CP_NICE];
    unsigned long long next_idle = load2[CP_IDLE];
    // Difference between the two
    unsigned long long diff_user = next_user - current_user;
    unsigned long long diff_system = next_system - current_system;
    unsigned long long diff_nice = next_nice - current_nice;
    unsigned long long diff_idle = next_idle - current_idle;

    return static_cast<float>(diff_user + diff_system + diff_nice) /
	  static_cast<float>(diff_user + diff_system + diff_nice + diff_idle) *
	  100.0;
}
<|endoftext|>


<commit_before>#include "bullet.hpp"

Bullet::Bullet(Party*p, sf::Texture*tex, sf::Image*collisionMap, int numStates, int numFrames, sf::Vector2f mov, Player* pl) :
    Character(p, tex, collisionMap, numStates, numFrames),
    player(pl) {
    setScale(sf::Vector2f(0.2f, 0.2f));
    setPosition(player->getPosition());
    this->mov = mov*speed;
    spriteSize = 64;
}

void Bullet::update(float deltaTime){
    collided = moveWithCollisions(mov*deltaTime);
    if(!collided) this->move(mov*deltaTime);
}

void Bullet::draw(sf::RenderWindow*window){
    window->draw(*this);
}

bool Bullet::hasCollided(){
    return collided;
}
<commit_msg>las balas salen de la boca<commit_after>#include "bullet.hpp"

Bullet::Bullet(Party*p, sf::Texture*tex, sf::Image*collisionMap, int numStates, int numFrames, sf::Vector2f mov, Player* pl) :
    Character(p, tex, collisionMap, numStates, numFrames),
    player(pl) {
    float scale = 0.2f;
    setScale(sf::Vector2f(scale,scale));
    setPosition(player->getPosition().x+player->getTextureRect().width*scale  , player->getPosition().y+player->getTextureRect().height*scale);
    this->mov = mov*speed;
    spriteSize = 64;
}

void Bullet::update(float deltaTime){
    collided = moveWithCollisions(mov*deltaTime);
    if(!collided) this->move(mov*deltaTime);
}

void Bullet::draw(sf::RenderWindow*window){
    window->draw(*this);
}

bool Bullet::hasCollided(){
    return collided;
}
<|endoftext|>


<commit_before>#pragma once

#include <charconv>

#pragma GCC diagnostic push
#ifdef __clang__
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wimplicit-int-conversion"
#pragma GCC diagnostic ignored "-Wshift-sign-overflow"
#pragma GCC diagnostic ignored "-Wundef"
#endif

#include <date/date.h>
#include <date/iso_week.h>

#pragma GCC diagnostic pop

#include "acmacs-base/fmt.hh"
// #include "acmacs-base/sfinae.hh"

// ----------------------------------------------------------------------

namespace date
{
    class date_parse_error : public std::runtime_error
    {
      public:
        using std::runtime_error::runtime_error;
    };

    using period_diff_t = int;

    enum class throw_on_error { no, yes };
    enum class allow_incomplete { no, yes };

    constexpr year_month_day invalid_date() { return year{0} / 0 / 0; }

    inline year_month_day today() { return floor<days>(std::chrono::system_clock::now()); }
    inline size_t current_year() { return static_cast<size_t>(static_cast<int>(today().year())); }

    inline auto get_year(const year_month_day& dt) { return static_cast<size_t>(static_cast<int>(dt.year())); }
    inline auto year_ok(const year_month_day& dt) { const auto ye = get_year(dt); return ye >= 1900 && ye <= current_year(); }
    inline auto get_month(const year_month_day& dt) { return static_cast<unsigned>(dt.month()); }
    inline auto month_ok(const year_month_day& dt) { const auto mont = get_month(dt); return mont > 0 && mont <= 12; }
    inline auto get_day(const year_month_day& dt) { return static_cast<unsigned>(dt.day()); }

    inline std::string display(const year_month_day& dt, const char* fmt = "%Y-%m-%d") { return dt.ok() ? format(fmt, dt) : std::string{"*invalid-date*"}; }

    inline std::string display(const year_month_day& dt, allow_incomplete allow)
    {
        if (dt.ok()) {
            return format("%Y-%m-%d", dt);
        }
        else if (allow == allow_incomplete::yes) {
            if (get_month(dt) == 0)
                return fmt::format("{}", get_year(dt));
            else if (get_day(dt) == 0)
                return fmt::format("{}-{:02d}", get_year(dt), get_month(dt));
            else
                return fmt::format("{}-{:02d}-{:02d}", get_year(dt), get_month(dt), get_day(dt));
        }
        else
            return fmt::format("*invalid-date: {}-{}-{}*", get_year(dt), get_month(dt), get_day(dt));
    }

    inline auto month_3(const year_month_day& dt) { return format("%b", dt); }
    inline auto year_2(const year_month_day& dt) { return format("%y", dt); }
    inline auto year_4(const year_month_day& dt) { return format("%Y", dt); }
    inline auto month3_year2(const year_month_day& dt) { return format("%b %y", dt); }
    inline auto monthtext(const year_month_day& dt) { return format("%B", dt); }
    inline auto monthtext_year(const year_month_day& dt) { return format("%B %Y", dt); }
    inline auto year4_month2(const year_month_day& dt) { return format("%Y-%m", dt); }
    inline auto year4_month2_day2(const year_month_day& dt) { return format("%Y-%m-%d", dt); }

    inline auto beginning_of_month(const year_month_day& dt) { unsigned mont = static_cast<unsigned>(dt.month()); if (mont == 0) mont = 7; return year_month_day(dt.year(), month{mont}, day{1}); }
    inline auto beginning_of_year(const year_month_day& dt) { return year_month_day(dt.year(), month{1}, day{1}); }
    inline auto beginning_of_week(const year_month_day& dt)
    {
        const auto make = [](const iso_week::year_weeknum_weekday& yw) { return year_month_day{iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{Monday})}; };
        if (static_cast<unsigned>(dt.day()) == 0) {
            if (static_cast<unsigned>(dt.month()) == 0)
                return make(iso_week::year_weeknum_weekday(dt.year() / month{7} / day{1}));
            else
                return make(iso_week::year_weeknum_weekday(dt.year() / dt.month() / day{1}));
        }
        else {
            return make(iso_week::year_weeknum_weekday(dt));
        }
    }

    // returns date for the last day of the year-month stored in this
    inline auto end_of_month(const year_month_day& dt) { return year_month_day(year_month_day_last(dt.year(), month_day_last(dt.month()))); }

    inline auto years_ago(const year_month_day& dt, period_diff_t number_of_years) { return dt - date::years(number_of_years); }
    inline auto months_ago(const year_month_day& dt, period_diff_t number_of_months) { return dt - date::months(number_of_months); }
    inline auto weeks_ago(const year_month_day& dt, period_diff_t number_of_weeks) { return static_cast<date::sys_days>(dt) - date::weeks(number_of_weeks); }
    inline auto days_ago(const year_month_day& dt, period_diff_t number_of_days) { return static_cast<date::sys_days>(dt) - date::days(number_of_days); }

    inline auto next_month(const year_month_day& dt) { return dt + date::months(1); }
    inline auto next_year(const year_month_day& dt) { return dt + date::years(1); }
    inline auto next_week(const year_month_day& dt) { return year_month_day{static_cast<sys_days>(dt) + date::weeks(1)}; }
    inline auto next_day(const year_month_day& dt) { return year_month_day{static_cast<sys_days>(dt) + date::days(1)}; }

    inline auto& increment_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt += date::months(number_of_months); return dt; }
    inline auto& decrement_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt -= date::months(number_of_months); return dt; }
    inline auto& increment_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt += date::years(number_of_years); return dt; }
    inline auto& decrement_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt -= date::years(number_of_years); return dt; }
    inline auto& increment_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) + date::weeks{number_of_weeks}; return dt; }
    inline auto& decrement_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) - date::weeks{number_of_weeks}; return dt; }
    inline auto& increment_day(year_month_day& dt, period_diff_t number_of_days = 1) { dt = static_cast<date::sys_days>(dt) + date::days{number_of_days}; return dt; }
    inline auto& decrement_day(year_month_day& dt, period_diff_t number_of_days = 1) { dt = static_cast<date::sys_days>(dt) - date::days{number_of_days}; return dt; }

    inline year_month_day from_string(std::string_view source, const char* fmt)
    {
            year_month_day result = invalid_date();
            std::istringstream in(std::string{source});
            if (from_stream(in, fmt, result)) {
                if (result.year() < year{30})
                    result += years(2000);
                else if (result.year() < year{100})
                    result += years(1900);
            }
            return result;
    }

    inline year_month_day from_string(std::string_view source, allow_incomplete allow = allow_incomplete::no, throw_on_error toe = throw_on_error::yes)
    {
        if (source.empty()) {
            if (toe == throw_on_error::yes)
                throw date_parse_error(fmt::format("cannot parse date from \"{}\"", source));
            return invalid_date();
        }

        for (const char* fmt : {"%Y-%m-%d", "%Y%m%d", "%m/%d/%Y", "%d/%m/%Y", "%Y/%m/%d", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"}) {
            // if (const auto result = from_string(std::forward<S>(source), fmt); result.ok())
            if (const auto result = from_string(source, fmt); result.ok())
                return result;
        }
        if (allow == allow_incomplete::yes) {
            for (const char* fmt : {"%Y-00-00", "%Y-%m-00", "%Y-%m", "%Y%m", "%Y"}) {
                // date lib cannot parse incomplete date
                constexpr int invalid = 99999;
                struct tm tm;
                tm.tm_mon = tm.tm_mday = invalid;
                if (strptime(source.data(), fmt, &tm) == &*source.end()) {
                    if (tm.tm_mon == invalid)
                        return year{tm.tm_year + 1900} / 0 / 0;
                    else
                        return year{tm.tm_year + 1900} / month{static_cast<unsigned>(tm.tm_mon) + 1} / 0;
                }
            }
        }
        if (toe == throw_on_error::yes)
            throw date_parse_error(fmt::format("cannot parse date from \"{}\" (allow_incomplete: {})", source, allow == allow_incomplete::yes));
        return invalid_date();
    }

    inline year year_from_string(std::string_view source)
    {
        int yr;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), yr); ec == std::errc{} && p == &*source.end())
            return year{yr};
        else
            return year{9999};
    }

    inline month month_from_string(std::string_view source)
    {
        unsigned mo;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), mo); ec == std::errc{} && p == &*source.end())
            return month{mo};
        else
            return month{99};
    }

    inline day day_from_string(std::string_view source)
    {
        unsigned dy;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), dy); ec == std::errc{} && p == &*source.end())
            return day{dy};
        else
            return day{99};
    }

    inline std::string current_date_time()
    {
        const auto now = std::chrono::system_clock::now();
        const auto in_time_t = std::chrono::system_clock::to_time_t(now);
        return fmt::format("{:%Y-%m-%d %H:%M:%S %Z}", *std::localtime(&in_time_t));
    }

    inline period_diff_t days_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<days>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t weeks_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<weeks>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t months_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<months>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t calendar_months_between_dates(const year_month_day& a, const year_month_day& b)
    {
        return std::chrono::duration_cast<months>(static_cast<sys_days>(beginning_of_month(b)) - static_cast<sys_days>(beginning_of_month(a))).count();
    }

    inline period_diff_t calendar_months_between_dates_inclusive(const year_month_day& a, const year_month_day& b) { return calendar_months_between_dates(a, b) + 1; }

    inline period_diff_t years_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<years>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

} // namespace date

// ----------------------------------------------------------------------

template <typename T> struct fmt::formatter<T, std::enable_if_t<std::is_base_of<date::year_month_day, T>::value, char>> : fmt::formatter<std::string> {
    template <typename FormatCtx> auto format(const date::year_month_day& dt, FormatCtx& ctx) { return fmt::formatter<std::string>::format(date::display(dt, date::allow_incomplete::yes), ctx); }
};

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>date parsing improvement<commit_after>#pragma once

#include <charconv>

#pragma GCC diagnostic push
#ifdef __clang__
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wimplicit-int-conversion"
#pragma GCC diagnostic ignored "-Wshift-sign-overflow"
#pragma GCC diagnostic ignored "-Wundef"
#endif

#include <date/date.h>
#include <date/iso_week.h>

#pragma GCC diagnostic pop

#include "acmacs-base/fmt.hh"
// #include "acmacs-base/sfinae.hh"

// ----------------------------------------------------------------------

namespace date
{
    class date_parse_error : public std::runtime_error
    {
      public:
        using std::runtime_error::runtime_error;
    };

    using period_diff_t = int;

    enum class throw_on_error { no, yes };
    enum class allow_incomplete { no, yes };
    enum class month_first { no, yes }; // European vs. US  if parts are separated by slashes

    constexpr year_month_day invalid_date() { return year{0} / 0 / 0; }

    inline year_month_day today() { return floor<days>(std::chrono::system_clock::now()); }
    inline size_t current_year() { return static_cast<size_t>(static_cast<int>(today().year())); }

    inline auto get_year(const year_month_day& dt) { return static_cast<size_t>(static_cast<int>(dt.year())); }
    inline auto year_ok(const year_month_day& dt) { const auto ye = get_year(dt); return ye >= 1900 && ye <= current_year(); }
    inline auto get_month(const year_month_day& dt) { return static_cast<unsigned>(dt.month()); }
    inline auto month_ok(const year_month_day& dt) { const auto mont = get_month(dt); return mont > 0 && mont <= 12; }
    inline auto get_day(const year_month_day& dt) { return static_cast<unsigned>(dt.day()); }

    inline std::string display(const year_month_day& dt, const char* fmt = "%Y-%m-%d") { return dt.ok() ? format(fmt, dt) : std::string{"*invalid-date*"}; }

    inline std::string display(const year_month_day& dt, allow_incomplete allow)
    {
        if (dt.ok()) {
            return format("%Y-%m-%d", dt);
        }
        else if (allow == allow_incomplete::yes) {
            if (get_month(dt) == 0)
                return fmt::format("{}", get_year(dt));
            else if (get_day(dt) == 0)
                return fmt::format("{}-{:02d}", get_year(dt), get_month(dt));
            else
                return fmt::format("{}-{:02d}-{:02d}", get_year(dt), get_month(dt), get_day(dt));
        }
        else
            return fmt::format("*invalid-date: {}-{}-{}*", get_year(dt), get_month(dt), get_day(dt));
    }

    inline auto month_3(const year_month_day& dt) { return format("%b", dt); }
    inline auto year_2(const year_month_day& dt) { return format("%y", dt); }
    inline auto year_4(const year_month_day& dt) { return format("%Y", dt); }
    inline auto month3_year2(const year_month_day& dt) { return format("%b %y", dt); }
    inline auto monthtext(const year_month_day& dt) { return format("%B", dt); }
    inline auto monthtext_year(const year_month_day& dt) { return format("%B %Y", dt); }
    inline auto year4_month2(const year_month_day& dt) { return format("%Y-%m", dt); }
    inline auto year4_month2_day2(const year_month_day& dt) { return format("%Y-%m-%d", dt); }

    inline auto beginning_of_month(const year_month_day& dt) { unsigned mont = static_cast<unsigned>(dt.month()); if (mont == 0) mont = 7; return year_month_day(dt.year(), month{mont}, day{1}); }
    inline auto beginning_of_year(const year_month_day& dt) { return year_month_day(dt.year(), month{1}, day{1}); }
    inline auto beginning_of_week(const year_month_day& dt)
    {
        const auto make = [](const iso_week::year_weeknum_weekday& yw) { return year_month_day{iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{Monday})}; };
        if (static_cast<unsigned>(dt.day()) == 0) {
            if (static_cast<unsigned>(dt.month()) == 0)
                return make(iso_week::year_weeknum_weekday(dt.year() / month{7} / day{1}));
            else
                return make(iso_week::year_weeknum_weekday(dt.year() / dt.month() / day{1}));
        }
        else {
            return make(iso_week::year_weeknum_weekday(dt));
        }
    }

    // returns date for the last day of the year-month stored in this
    inline auto end_of_month(const year_month_day& dt) { return year_month_day(year_month_day_last(dt.year(), month_day_last(dt.month()))); }

    inline auto years_ago(const year_month_day& dt, period_diff_t number_of_years) { return dt - date::years(number_of_years); }
    inline auto months_ago(const year_month_day& dt, period_diff_t number_of_months) { return dt - date::months(number_of_months); }
    inline auto weeks_ago(const year_month_day& dt, period_diff_t number_of_weeks) { return static_cast<date::sys_days>(dt) - date::weeks(number_of_weeks); }
    inline auto days_ago(const year_month_day& dt, period_diff_t number_of_days) { return static_cast<date::sys_days>(dt) - date::days(number_of_days); }

    inline auto next_month(const year_month_day& dt) { return dt + date::months(1); }
    inline auto next_year(const year_month_day& dt) { return dt + date::years(1); }
    inline auto next_week(const year_month_day& dt) { return year_month_day{static_cast<sys_days>(dt) + date::weeks(1)}; }
    inline auto next_day(const year_month_day& dt) { return year_month_day{static_cast<sys_days>(dt) + date::days(1)}; }

    inline auto& increment_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt += date::months(number_of_months); return dt; }
    inline auto& decrement_month(year_month_day& dt, period_diff_t number_of_months = 1) { dt -= date::months(number_of_months); return dt; }
    inline auto& increment_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt += date::years(number_of_years); return dt; }
    inline auto& decrement_year(year_month_day& dt, period_diff_t number_of_years = 1) { dt -= date::years(number_of_years); return dt; }
    inline auto& increment_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) + date::weeks{number_of_weeks}; return dt; }
    inline auto& decrement_week(year_month_day& dt, period_diff_t number_of_weeks = 1) { dt = static_cast<date::sys_days>(dt) - date::weeks{number_of_weeks}; return dt; }
    inline auto& increment_day(year_month_day& dt, period_diff_t number_of_days = 1) { dt = static_cast<date::sys_days>(dt) + date::days{number_of_days}; return dt; }
    inline auto& decrement_day(year_month_day& dt, period_diff_t number_of_days = 1) { dt = static_cast<date::sys_days>(dt) - date::days{number_of_days}; return dt; }

    inline year_month_day from_string(std::string_view source, const char* fmt)
    {
            year_month_day result = invalid_date();
            std::istringstream in(std::string{source});
            if (from_stream(in, fmt, result)) {
                if (result.year() < year{30})
                    result += years(2000);
                else if (result.year() < year{100})
                    result += years(1900);
            }
            return result;
    }

    inline year_month_day from_string(std::string_view source, allow_incomplete allow = allow_incomplete::no, throw_on_error toe = throw_on_error::yes, month_first mf = month_first::no)
    {
        if (source.empty()) {
            if (toe == throw_on_error::yes)
                throw date_parse_error(fmt::format("cannot parse date from \"{}\"", source));
            return invalid_date();
        }

        const auto fmt_order = [mf]() -> std::vector<const char*> {
            switch (mf) {
                case month_first::no:
                    return {"%Y-%m-%d", "%Y%m%d", "%d/%m/%Y", "%m/%d/%Y", "%Y/%m/%d", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"};
                case month_first::yes:
                    return {"%Y-%m-%d", "%Y%m%d", "%m/%d/%Y", "%d/%m/%Y", "%Y/%m/%d", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"};
            }
        };
        for (const char* fmt : fmt_order()) {
            // if (const auto result = from_string(std::forward<S>(source), fmt); result.ok())
            if (const auto result = from_string(source, fmt); result.ok())
                return result;
        }
        if (allow == allow_incomplete::yes) {
            for (const char* fmt : {"%Y-00-00", "%Y-%m-00", "%Y-%m", "%Y%m", "%Y"}) {
                // date lib cannot parse incomplete date
                constexpr int invalid = 99999;
                struct tm tm;
                tm.tm_mon = tm.tm_mday = invalid;
                if (strptime(source.data(), fmt, &tm) == &*source.end()) {
                    if (tm.tm_mon == invalid)
                        return year{tm.tm_year + 1900} / 0 / 0;
                    else
                        return year{tm.tm_year + 1900} / month{static_cast<unsigned>(tm.tm_mon) + 1} / 0;
                }
            }
        }
        if (toe == throw_on_error::yes)
            throw date_parse_error(fmt::format("cannot parse date from \"{}\" (allow_incomplete: {})", source, allow == allow_incomplete::yes));
        return invalid_date();
    }

    inline year year_from_string(std::string_view source)
    {
        int yr;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), yr); ec == std::errc{} && p == &*source.end())
            return year{yr};
        else
            return year{9999};
    }

    inline month month_from_string(std::string_view source)
    {
        unsigned mo;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), mo); ec == std::errc{} && p == &*source.end())
            return month{mo};
        else
            return month{99};
    }

    inline day day_from_string(std::string_view source)
    {
        unsigned dy;
        if (const auto [p, ec] = std::from_chars(&*source.begin(), &*source.end(), dy); ec == std::errc{} && p == &*source.end())
            return day{dy};
        else
            return day{99};
    }

    inline std::string current_date_time()
    {
        const auto now = std::chrono::system_clock::now();
        const auto in_time_t = std::chrono::system_clock::to_time_t(now);
        return fmt::format("{:%Y-%m-%d %H:%M:%S %Z}", *std::localtime(&in_time_t));
    }

    inline period_diff_t days_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<days>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t weeks_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<weeks>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t months_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<months>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

    inline period_diff_t calendar_months_between_dates(const year_month_day& a, const year_month_day& b)
    {
        return std::chrono::duration_cast<months>(static_cast<sys_days>(beginning_of_month(b)) - static_cast<sys_days>(beginning_of_month(a))).count();
    }

    inline period_diff_t calendar_months_between_dates_inclusive(const year_month_day& a, const year_month_day& b) { return calendar_months_between_dates(a, b) + 1; }

    inline period_diff_t years_between_dates(const year_month_day& a, const year_month_day& b) { return std::chrono::duration_cast<years>(static_cast<sys_days>(b) - static_cast<sys_days>(a)).count(); }

} // namespace date

// ----------------------------------------------------------------------

template <typename T> struct fmt::formatter<T, std::enable_if_t<std::is_base_of<date::year_month_day, T>::value, char>> : fmt::formatter<std::string> {
    template <typename FormatCtx> auto format(const date::year_month_day& dt, FormatCtx& ctx) { return fmt::formatter<std::string>::format(date::display(dt, date::allow_incomplete::yes), ctx); }
};

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>#pragma once

#include <iostream>

#include "acmacs-base/date2.hh"
#include "acmacs-base/week2.hh"

// ----------------------------------------------------------------------

class Date
{
 public:
    enum Today { Today };

    Date() : date_(date::year(9999), date::month(99), date::day(99)) {}         // invalid date by default
    Date(const Date&) = default;
    Date(enum Today) : date_{date::floor<date::days>(std::chrono::system_clock::now())} {}
    Date(const char* aText) { from_string(std::string(aText)); }
    Date(std::string aText) { from_string(aText); }
    Date(std::string_view aText) { from_string(aText); }
    Date(const date::year_month_day& src) : date_(src) {}
    Date(const date::sys_days& src) : date_(src) {}
    Date(int year, unsigned month, unsigned day) : date_(date::year(year), date::month(month), date::day(day)) {}
    static inline Date today() { return Today; }

    Date& operator=(const Date&) = default;
    Date& operator=(std::string aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(std::string_view aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(const char* aText) { if (aText && *aText) from_string(std::string_view(aText)); return *this; }

    Date months_ago(int number_of_months) const { return date_ - date::months(number_of_months); }
    Date years_ago(int number_of_years) const { return date_ - date::years(number_of_years); }
    Date weeks_ago(int number_of_weeks) const { return static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); }

    bool operator < (const Date& d) const { return date_ < d.date_; }
    bool operator == (const Date& d) const { return date_ == d.date_; }
    bool operator >= (const Date& d) const { return date_ >= d.date_; }

    bool empty() const { return !date_.ok(); }
    operator bool() const { return date_.ok(); }
    auto year() const { return static_cast<int>(date_.year()); }
    auto month() const { return static_cast<unsigned>(date_.month()); }
    auto day() const { return static_cast<unsigned>(date_.day()); }

    std::string display() const { return date_.ok() ? date::format("%Y-%m-%d", date_) : std::string{"*invalid-date*"}; }
    operator std::string() const { return display(); }

      // returns date for the 1st day of the year-month stored in this
    Date beginning_of_month() const { return date::year_month_day(date_.year(), date_.month(), date::day{1}); }
    Date beginning_of_year() const { return date::year_month_day(date_.year(), date::month{1}, date::day{1}); }
    Date beginning_of_week() const
        {
            iso_week::year_weeknum_weekday yw(date_);
            return {iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{date::Monday})};
        }

    Date& increment_month(int number_of_months = 1) { date_ += date::months(number_of_months); return *this; }
    Date& decrement_month(int number_of_months = 1) { date_ -= date::months(number_of_months); return *this; }
    Date& increment_year(int number_of_years = 1) { date_ += date::years(number_of_years); return *this; }
    Date& decrement_year(int number_of_years = 1) { date_ -= date::years(number_of_years); return *this; }
    Date& increment_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) + date::weeks(number_of_weeks); return *this; }
    Date& decrement_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); return *this; }

    Date next_month() const { return date_ + date::months(1); }
    Date next_year() const { return date_ + date::years(1); }
    Date next_week() const { return {static_cast<date::sys_days>(date_) + date::weeks(1)}; }

    std::string month_3() const { return date::format("%b", date_); }
    std::string year_2() const { return date::format("%y", date_); }
    std::string year_4() const { return date::format("%Y", date_); }
    std::string month3_year2() const { return date::format("%b %y", date_); }
    std::string monthtext_year() const { return date::format("%B %Y", date_); }
    std::string year4_month2() const { return date::format("%Y-%m", date_); }
    std::string year4_month2_day2() const { return date::format("%Y-%m-%d", date_); }

      //? const auto& gregorian() const { return date_; }

 private:
    date::year_month_day date_;

    void from_string(std::string_view source)
        {
            from_string(std::string(source));
        }

    void from_string(std::string source)
        {
            using namespace date::literals;
            date_ = 1999_y/99/99;
            for (const char* format : {"%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"}) {
                std::istringstream in(source);
                in >> date::parse(format, date_);
                if (in) {
                    if (date_.year() < date::year{30})
                        date_ += date::years(2000);
                    else if (date_.year() < date::year{100})
                        date_ += date::years(1900);
                    return;
                }
            }
            std::cerr << "ERROR: cannot parse date from \"" << source << "\"\n";
        }

 public:
    friend inline int days_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::days>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int weeks_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::weeks>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int months_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::months>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int years_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::years>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

}; // class Date

// ----------------------------------------------------------------------

inline std::ostream& operator << (std::ostream& out, const Date& aDate)
{
    return out << aDate.display();
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>current_date_time()<commit_after>#pragma once

#include <iostream>
#include <iomanip>

#include "acmacs-base/date2.hh"
#include "acmacs-base/week2.hh"

// ----------------------------------------------------------------------

class Date
{
 public:
    enum Today { Today };

    Date() : date_(date::year(9999), date::month(99), date::day(99)) {}         // invalid date by default
    Date(const Date&) = default;
    Date(enum Today) : date_{date::floor<date::days>(std::chrono::system_clock::now())} {}
    Date(const char* aText) { from_string(std::string(aText)); }
    Date(std::string aText) { from_string(aText); }
    Date(std::string_view aText) { from_string(aText); }
    Date(const date::year_month_day& src) : date_(src) {}
    Date(const date::sys_days& src) : date_(src) {}
    Date(int year, unsigned month, unsigned day) : date_(date::year(year), date::month(month), date::day(day)) {}
    static inline Date today() { return Today; }

    Date& operator=(const Date&) = default;
    Date& operator=(std::string aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(std::string_view aText) { if (!aText.empty()) from_string(aText); return *this; }
    Date& operator=(const char* aText) { if (aText && *aText) from_string(std::string_view(aText)); return *this; }

    Date months_ago(int number_of_months) const { return date_ - date::months(number_of_months); }
    Date years_ago(int number_of_years) const { return date_ - date::years(number_of_years); }
    Date weeks_ago(int number_of_weeks) const { return static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); }

    bool operator < (const Date& d) const { return date_ < d.date_; }
    bool operator == (const Date& d) const { return date_ == d.date_; }
    bool operator >= (const Date& d) const { return date_ >= d.date_; }

    bool empty() const { return !date_.ok(); }
    operator bool() const { return date_.ok(); }
    auto year() const { return static_cast<int>(date_.year()); }
    auto month() const { return static_cast<unsigned>(date_.month()); }
    auto day() const { return static_cast<unsigned>(date_.day()); }

    std::string display() const { return date_.ok() ? date::format("%Y-%m-%d", date_) : std::string{"*invalid-date*"}; }
    operator std::string() const { return display(); }

      // returns date for the 1st day of the year-month stored in this
    Date beginning_of_month() const { return date::year_month_day(date_.year(), date_.month(), date::day{1}); }
    Date beginning_of_year() const { return date::year_month_day(date_.year(), date::month{1}, date::day{1}); }
    Date beginning_of_week() const
        {
            iso_week::year_weeknum_weekday yw(date_);
            return {iso_week::year_weeknum_weekday(yw.year(), yw.weeknum(), iso_week::weekday{date::Monday})};
        }

    Date& increment_month(int number_of_months = 1) { date_ += date::months(number_of_months); return *this; }
    Date& decrement_month(int number_of_months = 1) { date_ -= date::months(number_of_months); return *this; }
    Date& increment_year(int number_of_years = 1) { date_ += date::years(number_of_years); return *this; }
    Date& decrement_year(int number_of_years = 1) { date_ -= date::years(number_of_years); return *this; }
    Date& increment_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) + date::weeks(number_of_weeks); return *this; }
    Date& decrement_week(int number_of_weeks = 1) { date_ = static_cast<date::sys_days>(date_) - date::weeks(number_of_weeks); return *this; }

    Date next_month() const { return date_ + date::months(1); }
    Date next_year() const { return date_ + date::years(1); }
    Date next_week() const { return {static_cast<date::sys_days>(date_) + date::weeks(1)}; }

    std::string month_3() const { return date::format("%b", date_); }
    std::string year_2() const { return date::format("%y", date_); }
    std::string year_4() const { return date::format("%Y", date_); }
    std::string month3_year2() const { return date::format("%b %y", date_); }
    std::string monthtext_year() const { return date::format("%B %Y", date_); }
    std::string year4_month2() const { return date::format("%Y-%m", date_); }
    std::string year4_month2_day2() const { return date::format("%Y-%m-%d", date_); }

      //? const auto& gregorian() const { return date_; }

 private:
    date::year_month_day date_;

    void from_string(std::string_view source)
        {
            from_string(std::string(source));
        }

    void from_string(std::string source)
        {
            using namespace date::literals;
            date_ = 1999_y/99/99;
            for (const char* format : {"%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y", "%B%n %d%n %Y", "%B %d,%n %Y", "%b%n %d%n %Y", "%b %d,%n %Y"}) {
                std::istringstream in(source);
                in >> date::parse(format, date_);
                if (in) {
                    if (date_.year() < date::year{30})
                        date_ += date::years(2000);
                    else if (date_.year() < date::year{100})
                        date_ += date::years(1900);
                    return;
                }
            }
            std::cerr << "ERROR: cannot parse date from \"" << source << "\"\n";
        }

 public:
    friend inline int days_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::days>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int weeks_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::weeks>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int months_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::months>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

    friend inline int years_between_dates(const Date& a, const Date& b)
        {
            return std::chrono::duration_cast<date::years>(static_cast<date::sys_days>(b.date_) - static_cast<date::sys_days>(a.date_)).count();
        }

}; // class Date

// ----------------------------------------------------------------------

inline std::ostream& operator << (std::ostream& out, const Date& aDate)
{
    return out << aDate.display();
}

// ----------------------------------------------------------------------

// https://stackoverflow.com/questions/17223096/outputting-date-and-time-in-c-using-stdchrono
inline std::string current_date_time()
{
    const auto now = std::chrono::system_clock::now();
    const auto in_time_t = std::chrono::system_clock::to_time_t(now);
    std::stringstream ss;
    ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %H:%M:%S %Z");
    return ss.str();
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>