<commit_before>/*
 * Copyright 2013 The Imaging Source Europe GmbH
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include "gsttiscolorize.h"

#include <string.h>
#include "bayer.h"
#include <libudev.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/videodev2.h>


GST_DEBUG_CATEGORY_STATIC (gst_tiscolorize_debug_category);
#define GST_CAT_DEFAULT gst_tiscolorize_debug_category

/* prototypes */


static void gst_tiscolorize_set_property (GObject* object,
                                          guint property_id,
                                          const GValue* value,
                                          GParamSpec* pspec);

static void gst_tiscolorize_get_property (GObject* object,
                                          guint property_id,
                                          GValue* value,
                                          GParamSpec* pspec);

static void gst_tiscolorize_finalize (GObject* object);

static GstFlowReturn gst_tiscolorize_transform_ip (GstBaseTransform* trans, GstBuffer* buf);
static GstCaps* gst_tiscolorize_transform_caps (GstBaseTransform* trans,
                                                GstPadDirection direction,
                                                GstCaps* caps);

static void gst_tiscolorize_fixate_caps (GstBaseTransform* base,
                                         GstPadDirection direction,
                                         GstCaps* caps,
                                         GstCaps* othercaps);

enum
{
    PROP_0,
    PROP_PATTERN,
};

/* pad templates */

static GstStaticPadTemplate gst_tiscolorize_sink_template =
    GST_STATIC_PAD_TEMPLATE ("sink",
                             GST_PAD_SINK,
                             GST_PAD_ALWAYS,
                             GST_STATIC_CAPS ("video/x-raw-gray,bpp=8,framerate=(fraction)[0/1,1000/1],width=[1,MAX],height=[1,MAX]")
        );

static GstStaticPadTemplate gst_tiscolorize_src_template =
    GST_STATIC_PAD_TEMPLATE ("src",
                             GST_PAD_SRC,
                             GST_PAD_ALWAYS,
                             GST_STATIC_CAPS ("video/x-raw-bayer,format=(string){bggr,grbg,gbrg,rggb},framerate=(fraction)[0/1,MAX],width=[1,MAX],height=[1,MAX]")
        );


/* class initialization */

G_DEFINE_TYPE_WITH_CODE (GstTisColorize, gst_tiscolorize, GST_TYPE_BASE_TRANSFORM,
                         GST_DEBUG_CATEGORY_INIT (gst_tiscolorize_debug_category, "tiscolorize", 0,
                                                  "debug category for tiscolorize element"));


static void gst_tiscolorize_class_init (GstTisColorizeClass* klass)
{
    GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
    GstBaseTransformClass* base_transform_class = GST_BASE_TRANSFORM_CLASS (klass);

    /* Setting up pads and setting metadata should be moved to
       base_class_init if you intend to subclass this class. */

    gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS(klass),
                                               &gst_tiscolorize_src_template);
    gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS(klass),
                                               &gst_tiscolorize_sink_template);


    gobject_class->set_property = gst_tiscolorize_set_property;
	gobject_class->get_property = gst_tiscolorize_get_property;
    gobject_class->finalize = gst_tiscolorize_finalize;
    base_transform_class->transform_ip = GST_DEBUG_FUNCPTR (gst_tiscolorize_transform_ip);
    base_transform_class->transform_caps = GST_DEBUG_FUNCPTR (gst_tiscolorize_transform_caps);
    base_transform_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_tiscolorize_fixate_caps);

    gst_element_class_set_details_simple (GST_ELEMENT_CLASS(klass),
                                          "The Imaging Source White Balance Element",
                                          "Generic",
                                          "Adjusts white balancing of RAW video data buffers",
                                          "<NAME> <<EMAIL>>");

	g_object_class_install_property (gobject_class,
                                     PROP_PATTERN,
                                     g_param_spec_string ("bayer-pattern",
                                                          "Bayer Pattern",
                                                          "Bayer Pattern to use:",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

}



void gst_tiscolorize_set_property (GObject* object,
                                   guint property_id,
                                   const GValue* value,
                                   GParamSpec* pspec)
{
    GstTisColorize* self = GST_TISCOLORIZE (object);

    switch (property_id)
    {
        case PROP_PATTERN:
            g_free(self->pattern);
            self->pattern = g_strdup(g_value_get_string( value));
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
            break;
    }
}


void gst_tiscolorize_get_property (GObject* object,
                                     guint property_id,
                                     GValue* value,
                                     GParamSpec* pspec)
{
    GstTisColorize* self = GST_TISCOLORIZE (object);

    switch (property_id)
    {
        case PROP_PATTERN:
            g_value_set_string (value, self->pattern);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
            break;
    }
}



static void gst_tiscolorize_init (GstTisColorize* self)
{
    self->i = 0;
    self->pattern = NULL;
}


void gst_tiscolorize_finalize (GObject* object)
{
    GstTisColorize* self = (GstTisColorize*) object;
    g_free(self->pattern);

    G_OBJECT_CLASS (gst_tiscolorize_parent_class)->finalize (object);
}


typedef struct
{
    const char* device_name;
    unsigned int device_id;
    tBY8Pattern bayer_pattern;
} dev_info;


typedef struct
{
    char devnode [100];
    unsigned int productid;
    unsigned int serial;
} usb_cam;


dev_info device_info [] =
{
    {
        "DFx 72BUC02",
        8307,
        GR
    },
    {
        "DFx 72BUC02",
        8207,
        GR
    },
    {
        "DFx 22BUC02",
        8302,
        GB
    },
    {
        "DFx 22BUC02",
        8202,
        GB
    },
    {
        "DFx 42UC03",
        8308,
        GR
    },
    {
        "DFx 42UC03",
        8208,
        GR
    }
};


#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))

void get_usb_cameras (usb_cam (*cameras)[], int* camera_count)
{

    struct udev* udev;
    struct udev_enumerate* enumerate;
    struct udev_list_entry* devices;
    struct udev_list_entry* dev_list_entry;
    struct udev_device* dev;

    int camera_cnt = 0;
    (*camera_count) = 0;

    udev = udev_new();
    if (!udev)
    {
        gst_debug_log (gst_tiscolorize_debug_category,
                       GST_LEVEL_ERROR,
                       "tiscolorize",
                       "get_usbs_cameras",
                       184,
                       NULL,
                       "Unable to create udev object");

        return;
    }

    /* Create a list of the devices in the 'video4linux' subsystem. */
    enumerate = udev_enumerate_new(udev);
    udev_enumerate_add_match_subsystem(enumerate, "video4linux");
    udev_enumerate_scan_devices(enumerate);
    devices = udev_enumerate_get_list_entry(enumerate);

    udev_list_entry_foreach(dev_list_entry, devices)
    {
        const char* path;
        char needed_path[100];

        /* Get the filename of the /sys entry for the device
           and create a udev_device object (dev) representing it */
        path = udev_list_entry_get_name(dev_list_entry);
        dev = udev_device_new_from_syspath(udev, path);

        /* The device pointed to by dev contains information about
           the hidraw device. In order to get information about the
           USB device, get the parent device with the
           subsystem/devtype pair of "usb"/"usb_device". This will
           be several levels up the tree, but the function will find
           it.*/

        /* we need to copy the devnode (/dev/videoX) before the path
           is changed to the path of the usb device behind it (/sys/class/....) */
        strcpy(needed_path, udev_device_get_devnode(dev));

        dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");

        if (!dev)
        {
            gst_debug_log (gst_tiscolorize_debug_category,
                           GST_LEVEL_ERROR,
                           "tiscolorize",
                           "get_usbs_cameras",
                           184,
                           NULL,
                           "Unable to retrieve usb device for %s", needed_path);
            return;
        }

        /* From here, we can call get_sysattr_value() for each file
           in the device's /sys entry. The strings passed into these
           functions (idProduct, idVendor, serial, etc.) correspond
           directly to the files in the directory which represents
           the USB device. Note that USB strings are Unicode, UCS2
           encoded, but the strings returned from
           udev_device_get_sysattr_value() are UTF-8 encoded. */

        if (strcmp(udev_device_get_sysattr_value(dev, "idVendor"), "199e") == 0)
        {

            strcpy((*cameras)[camera_cnt].devnode, needed_path);
            (*cameras)[camera_cnt].productid = atoi(udev_device_get_sysattr_value(dev, "idProduct"));
            (*cameras)[camera_cnt].serial = atoi(udev_device_get_sysattr_value(dev, "serial"));

            gst_debug_log (gst_tiscolorize_debug_category,
                           GST_LEVEL_INFO,
                           "tiscolorize",
                           "get_usbs_cameras",
                           250,
                           NULL,
                           "Found v4l2 device %s", needed_path);

            camera_cnt++;
        }
        udev_device_unref(dev);
    }

    (*camera_count) = camera_cnt;
    /* Free the enumerator object */
    udev_enumerate_unref(enumerate);

    udev_unref(udev);

}


int get_product_id (char* devnode)
{
    usb_cam cameras [100];
    int camera_cnt = 0;
    get_usb_cameras(&cameras, &camera_cnt);
    int i;
    for (i = 0; i < camera_cnt; ++i)
    {
        if (strcmp(devnode, cameras[i].devnode) == 0)
        {
            gst_debug_log (gst_tiscolorize_debug_category,
                           GST_LEVEL_INFO,
                           "tiscolorize",
                           "get_product_id",
                           280,
                           NULL,
                           "Found product id %d for device %s", cameras[i].productid, cameras[i].devnode);

            return cameras[i].productid;
        }
    }
    return 0;
}


char* device_bayer_pattern (int id)
{
    unsigned int i;
    for (i = 0; i < ARRAYSIZE(device_info); i++)
    {
        if (device_info[i].device_id == id)
        {
            return bayer_to_string(device_info[i].bayer_pattern);
        }
    }

    return NULL;
}


int check_bayer_pattern (char* desc)
{
    if (strcmp(desc, "47425247-0000-0010-8000-00aa003") == 0 || strcmp(desc, "GRBG Bayer (GRBG)"))
    {
        return GR;
    }
    else if (strcmp(desc, "42474752-0000-0010-8000-00aa003") == 0 || strcmp(desc, "RGGB Bayer (RGGB)"))
    {
        return RG;
    }
    else if (strcmp(desc, "47524247-0000-0010-8000-00aa003") == 0 || strcmp(desc, "RGGB Bayer (GBRG)"))
    {
        return GB;
    }
    else if (strcmp(desc, "31384142-0000-0010-8000-00aa003") == 0 || strcmp(desc, "BGGR Bayer (BGGR)"))
    {
        return BG;
    }
    else
    {
        return -1;
    }
}


static GstCaps* gst_tiscolorize_transform_caps (GstBaseTransform* trans,<commit_msg>Fixed faulty comparison for bayer patter string descriptions<commit_after>/*
 * Copyright 2013 The Imaging Source Europe GmbH
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include "gsttiscolorize.h"

#include <string.h>
#include "bayer.h"
#include <libudev.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/videodev2.h>


GST_DEBUG_CATEGORY_STATIC (gst_tiscolorize_debug_category);
#define GST_CAT_DEFAULT gst_tiscolorize_debug_category

/* prototypes */


static void gst_tiscolorize_set_property (GObject* object,
                                          guint property_id,
                                          const GValue* value,
                                          GParamSpec* pspec);

static void gst_tiscolorize_get_property (GObject* object,
                                          guint property_id,
                                          GValue* value,
                                          GParamSpec* pspec);

static void gst_tiscolorize_finalize (GObject* object);

static GstFlowReturn gst_tiscolorize_transform_ip (GstBaseTransform* trans, GstBuffer* buf);
static GstCaps* gst_tiscolorize_transform_caps (GstBaseTransform* trans,
                                                GstPadDirection direction,
                                                GstCaps* caps);

static void gst_tiscolorize_fixate_caps (GstBaseTransform* base,
                                         GstPadDirection direction,
                                         GstCaps* caps,
                                         GstCaps* othercaps);

enum
{
    PROP_0,
    PROP_PATTERN,
};

/* pad templates */

static GstStaticPadTemplate gst_tiscolorize_sink_template =
    GST_STATIC_PAD_TEMPLATE ("sink",
                             GST_PAD_SINK,
                             GST_PAD_ALWAYS,
                             GST_STATIC_CAPS ("video/x-raw-gray,bpp=8,framerate=(fraction)[0/1,1000/1],width=[1,MAX],height=[1,MAX]")
        );

static GstStaticPadTemplate gst_tiscolorize_src_template =
    GST_STATIC_PAD_TEMPLATE ("src",
                             GST_PAD_SRC,
                             GST_PAD_ALWAYS,
                             GST_STATIC_CAPS ("video/x-raw-bayer,format=(string){bggr,grbg,gbrg,rggb},framerate=(fraction)[0/1,MAX],width=[1,MAX],height=[1,MAX]")
        );


/* class initialization */

G_DEFINE_TYPE_WITH_CODE (GstTisColorize, gst_tiscolorize, GST_TYPE_BASE_TRANSFORM,
                         GST_DEBUG_CATEGORY_INIT (gst_tiscolorize_debug_category, "tiscolorize", 0,
                                                  "debug category for tiscolorize element"));


static void gst_tiscolorize_class_init (GstTisColorizeClass* klass)
{
    GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
    GstBaseTransformClass* base_transform_class = GST_BASE_TRANSFORM_CLASS (klass);

    /* Setting up pads and setting metadata should be moved to
       base_class_init if you intend to subclass this class. */

    gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS(klass),
                                               &gst_tiscolorize_src_template);
    gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS(klass),
                                               &gst_tiscolorize_sink_template);


    gobject_class->set_property = gst_tiscolorize_set_property;
	gobject_class->get_property = gst_tiscolorize_get_property;
    gobject_class->finalize = gst_tiscolorize_finalize;
    base_transform_class->transform_ip = GST_DEBUG_FUNCPTR (gst_tiscolorize_transform_ip);
    base_transform_class->transform_caps = GST_DEBUG_FUNCPTR (gst_tiscolorize_transform_caps);
    base_transform_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_tiscolorize_fixate_caps);

    gst_element_class_set_details_simple (GST_ELEMENT_CLASS(klass),
                                          "The Imaging Source White Balance Element",
                                          "Generic",
                                          "Adjusts white balancing of RAW video data buffers",
                                          "<NAME> <<EMAIL>>");

	g_object_class_install_property (gobject_class,
                                     PROP_PATTERN,
                                     g_param_spec_string ("bayer-pattern",
                                                          "Bayer Pattern",
                                                          "Bayer Pattern to use:",
                                                          NULL,
                                                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

}



void gst_tiscolorize_set_property (GObject* object,
                                   guint property_id,
                                   const GValue* value,
                                   GParamSpec* pspec)
{
    GstTisColorize* self = GST_TISCOLORIZE (object);

    switch (property_id)
    {
        case PROP_PATTERN:
            g_free(self->pattern);
            self->pattern = g_strdup(g_value_get_string( value));
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
            break;
    }
}


void gst_tiscolorize_get_property (GObject* object,
                                     guint property_id,
                                     GValue* value,
                                     GParamSpec* pspec)
{
    GstTisColorize* self = GST_TISCOLORIZE (object);

    switch (property_id)
    {
        case PROP_PATTERN:
            g_value_set_string (value, self->pattern);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
            break;
    }
}



static void gst_tiscolorize_init (GstTisColorize* self)
{
    self->i = 0;
    self->pattern = NULL;
}


void gst_tiscolorize_finalize (GObject* object)
{
    GstTisColorize* self = (GstTisColorize*) object;
    g_free(self->pattern);

    G_OBJECT_CLASS (gst_tiscolorize_parent_class)->finalize (object);
}


typedef struct
{
    const char* device_name;
    unsigned int device_id;
    tBY8Pattern bayer_pattern;
} dev_info;


typedef struct
{
    char devnode [100];
    unsigned int productid;
    unsigned int serial;
} usb_cam;


dev_info device_info [] =
{
    {
        "DFx 72BUC02",
        8307,
        GR
    },
    {
        "DFx 72BUC02",
        8207,
        GR
    },
    {
        "DFx 22BUC02",
        8302,
        GB
    },
    {
        "DFx 22BUC02",
        8202,
        GB
    },
    {
        "DFx 42UC03",
        8308,
        GR
    },
    {
        "DFx 42UC03",
        8208,
        GR
    }
};


#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))

void get_usb_cameras (usb_cam (*cameras)[], int* camera_count)
{

    struct udev* udev;
    struct udev_enumerate* enumerate;
    struct udev_list_entry* devices;
    struct udev_list_entry* dev_list_entry;
    struct udev_device* dev;

    int camera_cnt = 0;
    (*camera_count) = 0;

    udev = udev_new();
    if (!udev)
    {
        gst_debug_log (gst_tiscolorize_debug_category,
                       GST_LEVEL_ERROR,
                       "tiscolorize",
                       "get_usbs_cameras",
                       184,
                       NULL,
                       "Unable to create udev object");

        return;
    }

    /* Create a list of the devices in the 'video4linux' subsystem. */
    enumerate = udev_enumerate_new(udev);
    udev_enumerate_add_match_subsystem(enumerate, "video4linux");
    udev_enumerate_scan_devices(enumerate);
    devices = udev_enumerate_get_list_entry(enumerate);

    udev_list_entry_foreach(dev_list_entry, devices)
    {
        const char* path;
        char needed_path[100];

        /* Get the filename of the /sys entry for the device
           and create a udev_device object (dev) representing it */
        path = udev_list_entry_get_name(dev_list_entry);
        dev = udev_device_new_from_syspath(udev, path);

        /* The device pointed to by dev contains information about
           the hidraw device. In order to get information about the
           USB device, get the parent device with the
           subsystem/devtype pair of "usb"/"usb_device". This will
           be several levels up the tree, but the function will find
           it.*/

        /* we need to copy the devnode (/dev/videoX) before the path
           is changed to the path of the usb device behind it (/sys/class/....) */
        strcpy(needed_path, udev_device_get_devnode(dev));

        dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");

        if (!dev)
        {
            gst_debug_log (gst_tiscolorize_debug_category,
                           GST_LEVEL_ERROR,
                           "tiscolorize",
                           "get_usbs_cameras",
                           184,
                           NULL,
                           "Unable to retrieve usb device for %s", needed_path);
            return;
        }

        /* From here, we can call get_sysattr_value() for each file
           in the device's /sys entry. The strings passed into these
           functions (idProduct, idVendor, serial, etc.) correspond
           directly to the files in the directory which represents
           the USB device. Note that USB strings are Unicode, UCS2
           encoded, but the strings returned from
           udev_device_get_sysattr_value() are UTF-8 encoded. */

        if (strcmp(udev_device_get_sysattr_value(dev, "idVendor"), "199e") == 0)
        {

            strcpy((*cameras)[camera_cnt].devnode, needed_path);
            (*cameras)[camera_cnt].productid = atoi(udev_device_get_sysattr_value(dev, "idProduct"));
            (*cameras)[camera_cnt].serial = atoi(udev_device_get_sysattr_value(dev, "serial"));

            gst_debug_log (gst_tiscolorize_debug_category,
                           GST_LEVEL_INFO,
                           "tiscolorize",
                           "get_usbs_cameras",
                           250,
                           NULL,
                           "Found v4l2 device %s", needed_path);

            camera_cnt++;
        }
        udev_device_unref(dev);
    }

    (*camera_count) = camera_cnt;
    /* Free the enumerator object */
    udev_enumerate_unref(enumerate);

    udev_unref(udev);

}


int get_product_id (char* devnode)
{
    usb_cam cameras [100];
    int camera_cnt = 0;
    get_usb_cameras(&cameras, &camera_cnt);
    int i;
    for (i = 0; i < camera_cnt; ++i)
    {
        if (strcmp(devnode, cameras[i].devnode) == 0)
        {
            gst_debug_log (gst_tiscolorize_debug_category,
                           GST_LEVEL_INFO,
                           "tiscolorize",
                           "get_product_id",
                           280,
                           NULL,
                           "Found product id %d for device %s", cameras[i].productid, cameras[i].devnode);

            return cameras[i].productid;
        }
    }
    return 0;
}


char* device_bayer_pattern (int id)
{
    unsigned int i;
    for (i = 0; i < ARRAYSIZE(device_info); i++)
    {
        if (device_info[i].device_id == id)
        {
            return bayer_to_string(device_info[i].bayer_pattern);
        }
    }

    return NULL;
}


int check_bayer_pattern (char* desc)
{
    if (strcmp(desc, "47425247-0000-0010-8000-00aa003") == 0 || strcmp(desc, "GRBG Bayer (GRBG)") == 0)
    {
        return GR;
    }
    else if (strcmp(desc, "42474752-0000-0010-8000-00aa003") == 0 || strcmp(desc, "RGGB Bayer (RGGB)") == 0)
    {
        return RG;
    }
    else if (strcmp(desc, "47524247-0000-0010-8000-00aa003") == 0 || strcmp(desc, "RGGB Bayer (GBRG)") == 0)
    {
        return GB;
    }
    else if (strcmp(desc, "31384142-0000-0010-8000-00aa003") == 0 || strcmp(desc, "BGGR Bayer (BGGR)") == 0)
    {
        return BG;
    }
    else
    {
        return -1;
    }
}


static GstCaps* gst_tiscolorize_transform_caps (GstBaseTransform* trans,
{'fraction_non_alphanumeric': 0.08385130052654499, 'fraction_numerical': 0.017498713329902212, 'mean_word_length': 1.7973421926910298, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 104, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7109554', 'n_tokens_mistral': 7931, 'n_tokens_neox': 7370, 'n_words': 1640}